# マスタードキュメント - 圃場管理編

> **最終更新**: 2026-02-20
> **対象バージョン**: Phase 1 (MVP) 完了時点
> **目的**: このドキュメントだけで圃場管理機能の全容を把握できること

---

## 目次

1. [機能概要](#1-機能概要)
2. [データモデル](#2-データモデル)
3. [API仕様](#3-api仕様)
4. [画面仕様](#4-画面仕様)
5. [インポート仕様](#5-インポート仕様)
6. [エクスポート仕様](#6-エクスポート仕様)
7. [対応表(E-2)機能](#7-対応表e-2機能)
8. [PDF帳票との関係](#8-pdf帳票との関係)
9. [作付け計画との関係](#9-作付け計画との関係)
10. [設計判断と制約](#10-設計判断と制約)
11. [ソースファイル索引](#11-ソースファイル索引)

---

## 1. 機能概要

### 圃場管理機能とは

農業生産者が所有・管理する農地（圃場）の情報を管理する機能。本システムでは以下の3種類のデータを扱う。

| データ種別 | 説明 | レコード数 | ソース |
|---|---|---|---|
| **実圃場（Field）** | 農家が実際に管理している農地 | 39筆 | 吉田農地台帳.ods |
| **共済マスタ（OfficialKyosaiField）** | 水稲共済細目書の区画 | 31区画 | 水稲共済細目用.ods |
| **中山間マスタ（OfficialChusankanField）** | 中山間交付金の区画 | 71区画 | 中山間.ods |

### 3つのデータの関係

```
実圃場 (39筆)
  ├── M:N ─── 共済マスタ (31区画)   ※1つの実圃場が複数の共済区画に対応可
  └── M:N ─── 中山間マスタ (71区画)  ※1つの実圃場が複数の中山間区画に対応可
```

**M:N (多対多) 関係にした理由**: 当初は1:1を想定したが、実運用データで「1つの実圃場が複数の申請区画にまたがる」ケースが判明したため。

### ユーザーフロー

1. **初回セットアップ**: データ取込画面から3つのODSファイルをインポート
2. **日常管理**: 圃場一覧で圃場情報の確認、圃場詳細で共済/中山間との紐付け確認・編集
3. **年次作業**: 作付け計画画面で年度ごとの作付けを設定
4. **申請書作成**: 帳票出力画面でPDFを生成・ダウンロード

---

## 2. データモデル

### 2.1 Field（実圃場）

**テーブル名**: `fields_field`
**ソース**: `backend/apps/fields/models.py:48-78`

| フィールド名 | 型 | 制約 | 説明 |
|---|---|---|---|
| id | BigAutoField | PK | 自動採番 |
| name | CharField(100) | NOT NULL | 圃場名（例: 「田んぼA」）|
| address | CharField(255) | NOT NULL | 住所 |
| area_tan | DecimalField(6,4) | NOT NULL | 面積（反）※表示用 |
| area_m2 | IntegerField | NOT NULL | 面積（m2）※計算用 |
| owner_name | CharField(100) | NOT NULL | 地主名 |
| group_name | CharField(50) | NULL可 | グループ名（エリア・用途分け用） |
| display_order | IntegerField | default=0 | 表示順（カスタム並び替え用） |
| raw_kyosai_k_num | CharField(20) | NULL可 | インポート時の共済耕地番号（raw値） |
| raw_kyosai_s_num | CharField(20) | NULL可 | インポート時の共済分筆番号（raw値） |
| raw_chusankan_id | CharField(20) | NULL可 | インポート時の中山間ID（raw値、カンマ区切り複数可） |
| location | PointField | NULL可 | 位置情報（Phase 1未使用） |

**M:N関係（中間テーブル）**:
- `fields_field_kyosai_fields` → OfficialKyosaiField との紐付け
- `fields_field_chusankan_fields` → OfficialChusankanField との紐付け

**面積の変換ルール**: `area_m2 = area_tan × 1000`（1反 = 1000m2 で統一）

### 2.2 OfficialKyosaiField（共済マスタ）

**テーブル名**: `fields_officialkyosaifield`
**ソース**: `backend/apps/fields/models.py:5-18`

| フィールド名 | 型 | 制約 | 説明 |
|---|---|---|---|
| id | BigAutoField | PK | 自動採番 |
| k_num | CharField(20) | UNIQUE(k_num, s_num) | 耕地番号 |
| s_num | CharField(20) | UNIQUE(k_num, s_num) | 分筆番号（枝番） |
| address | CharField(255) | NOT NULL | 住所（地名地番） |
| kanji_name | CharField(100) | NOT NULL | 漢字地名 |
| area | IntegerField | default=0 | 本地面積（m2）|

**ユニーク制約**: `(k_num, s_num)` の組み合わせで一意

### 2.3 OfficialChusankanField（中山間マスタ）

**テーブル名**: `fields_officialchusankanfield`
**ソース**: `backend/apps/fields/models.py:21-45`

| フィールド名 | 型 | 制約 | 説明 |
|---|---|---|---|
| id | BigAutoField | PK | 自動採番 |
| c_id | CharField(20) | UNIQUE | 中山間ID |
| chusankan_flag | CharField(10) | NULL可 | 中山間フラグ |
| oaza | CharField(100) | NOT NULL | 大字 |
| aza | CharField(100) | NOT NULL | 字 |
| chiban | CharField(50) | NOT NULL | 地番 |
| branch_num | CharField(20) | NULL可 | 枝番 |
| land_type | CharField(20) | NULL可 | 地目 |
| area | IntegerField | default=0 | 農地面積（m2） |
| planting_area | IntegerField | NULL可 | 植栽面積（m2） |
| original_crop | CharField(100) | NULL可 | 作付け品目 |
| manager | CharField(100) | NULL可 | 協定管理者 |
| owner | CharField(100) | NULL可 | 所有者 |
| slope | CharField(20) | NULL可 | 傾斜度 |
| base_amount | IntegerField | NULL可 | 基本金額 |
| steep_slope_addition | IntegerField | NULL可 | 超急傾斜加算額 |
| smart_agri_addition | IntegerField | NULL可 | スマート農業加算額 |
| payment_amount | IntegerField | NULL可 | 交付金額 |

**ユニーク制約**: `c_id` で一意

### 2.4 関連モデル（作付け計画系）

**Crop（作物マスタ）** - `backend/apps/plans/models.py:5-13`

| フィールド名 | 型 | 制約 | 説明 |
|---|---|---|---|
| id | BigAutoField | PK | 自動採番 |
| name | CharField(100) | UNIQUE | 作物名（米、トウモロコシ等） |

**Variety（品種マスタ）** - `backend/apps/plans/models.py:16-26`

| フィールド名 | 型 | 制約 | 説明 |
|---|---|---|---|
| id | BigAutoField | PK | 自動採番 |
| crop | FK(Crop) | CASCADE | 親の作物 |
| name | CharField(100) | UNIQUE(crop, name) | 品種名 |

**Plan（作付け計画）** - `backend/apps/plans/models.py:29-43`

| フィールド名 | 型 | 制約 | 説明 |
|---|---|---|---|
| id | BigAutoField | PK | 自動採番 |
| field | FK(Field) | CASCADE | 対象圃場 |
| year | IntegerField | UNIQUE(field, year) | 作付年度 |
| crop | FK(Crop) | CASCADE | 作物 |
| variety | FK(Variety) | SET_NULL, NULL可 | 品種 |
| notes | TextField | NULL可 | 備考 |

### 2.5 ER図（テキスト形式）

```
┌──────────────────┐     M:N      ┌──────────────────────┐
│ Field            │──────────────│ OfficialKyosaiField  │
│ (39筆)           │              │ (31区画)             │
│                  │     M:N      ├──────────────────────┤
│  id              │──────────────│ OfficialChusankanField│
│  name            │              │ (71区画)             │
│  address         │              └──────────────────────┘
│  area_tan        │
│  area_m2         │     1:N
│  owner_name      │──────────────┐
│  group_name      │              │
│  display_order   │    ┌─────────┴─────┐
└──────────────────┘    │ Plan          │
                        │ (年度×圃場)   │
                        │               │
                        │  field (FK)   │
                        │  year         │     N:1     ┌────────┐
                        │  crop (FK)    │─────────────│ Crop   │
                        │  variety (FK) │     N:1     ├────────┤
                        └───────────────┘─────────────│Variety │
                                                      └────────┘
```

---

## 3. API仕様

### 3.1 圃場 CRUD

**ベースURL**: `/api/fields/`
**ViewSet**: `FieldViewSet` (`backend/apps/fields/views.py:18-27`)
**認証**: JWT Bearer トークン必須

| メソッド | エンドポイント | 説明 | リクエスト/レスポンス |
|---|---|---|---|
| GET | `/api/fields/` | 圃場一覧取得 | → `Field[]`（group_name, display_order, id順） |
| GET | `/api/fields/{id}/` | 圃場詳細取得 | → `Field`（kyosai_fields, chusankan_fieldsをネスト含む） |
| POST | `/api/fields/` | 圃場新規作成 | ← `{name, address, area_tan, area_m2, owner_name}` |
| PUT | `/api/fields/{id}/` | 圃場全体更新 | ← 全フィールド |
| PATCH | `/api/fields/{id}/` | 圃場部分更新 | ← 変更フィールドのみ |
| DELETE | `/api/fields/{id}/` | 圃場削除 | → 204 No Content |

**レスポンス例（GET /api/fields/{id}/）**:
```json
{
  "id": 1,
  "name": "田んぼA",
  "address": "○○市△△町123",
  "area_tan": "1.2000",
  "area_m2": 1200,
  "owner_name": "吉田太郎",
  "group_name": "上エリア",
  "display_order": 1,
  "kyosai_fields": [
    {
      "id": 5,
      "k_num": "12",
      "s_num": "1",
      "address": "○○ 123",
      "kanji_name": "上田",
      "area": 1200,
      "linked_field_names": ["田んぼA"]
    }
  ],
  "chusankan_fields": [
    {
      "id": 10,
      "c_id": "42",
      "chusankan_flag": "○",
      "oaza": "△△",
      "aza": "□□",
      "chiban": "123",
      "branch_num": null,
      "land_type": "田",
      "area": 1200,
      "planting_area": 1100,
      "original_crop": "米",
      "manager": "吉田太郎",
      "owner": "吉田太郎",
      "slope": "1/20",
      "base_amount": 21000,
      "steep_slope_addition": null,
      "smart_agri_addition": null,
      "payment_amount": 21000,
      "linked_field_names": ["田んぼA"]
    }
  ]
}
```

**ソート**: デフォルトは `group_name ASC NULLS FIRST, display_order ASC, id ASC`
クエリパラメータ `ordering=area_tan` 等で変更可能。

### 3.2 共済/中山間マスタ（読み取り専用）

| メソッド | エンドポイント | 説明 |
|---|---|---|
| GET | `/api/kyosai-fields/` | 共済マスタ一覧（k_num, s_num順） |
| GET | `/api/kyosai-fields/{id}/` | 共済マスタ詳細 |
| GET | `/api/chusankan-fields/` | 中山間マスタ一覧（c_id順） |
| GET | `/api/chusankan-fields/{id}/` | 中山間マスタ詳細 |

**ViewSet**: `OfficialKyosaiFieldViewSet`, `OfficialChusankanFieldViewSet` (`backend/apps/fields/views.py:30-39`)
**ルーティング**: `backend/keinasystem/urls.py:23-25` の `master_router` で登録

各レスポンスに `linked_field_names: string[]` が含まれ、紐付いている実圃場の名前を返す。

### 3.3 M:N紐付け操作

**ソース**: `backend/apps/fields/views.py:42-77`

| メソッド | エンドポイント | 説明 |
|---|---|---|
| POST | `/api/fields/{field_id}/kyosai-links/` | 共済リンク追加 |
| DELETE | `/api/fields/{field_id}/kyosai-links/{kyosai_id}/` | 共済リンク削除 |
| POST | `/api/fields/{field_id}/chusankan-links/` | 中山間リンク追加 |
| DELETE | `/api/fields/{field_id}/chusankan-links/{chusankan_id}/` | 中山間リンク削除 |

**リンク追加リクエスト例**:
```json
// POST /api/fields/1/kyosai-links/
{ "kyosai_field_ids": [5, 8] }

// POST /api/fields/1/chusankan-links/
{ "chusankan_field_ids": [10, 15, 20] }
```

### 3.4 作付け計画関連API

**ベースURL**: `/api/plans/`
**ViewSet**: `PlanViewSet` (`backend/apps/plans/views.py:20-132`)

| メソッド | エンドポイント | 説明 |
|---|---|---|
| GET | `/api/plans/?year=2026` | 年度指定で計画一覧取得 |
| POST | `/api/plans/` | 計画新規作成 |
| PATCH | `/api/plans/{id}/` | 計画更新 |
| DELETE | `/api/plans/{id}/` | 計画削除 |
| GET | `/api/plans/summary/?year=2026` | 年度別集計 |
| POST | `/api/plans/copy_from_previous_year/` | 前年度コピー |
| POST | `/api/plans/bulk_update/` | 一括更新 |
| GET | `/api/plans/get_crops_with_varieties/` | 作物+品種一覧 |

**集計レスポンス例 (GET /api/plans/summary/?year=2026)**:
```json
{
  "year": 2026,
  "total_fields": 39,
  "assigned_fields": 35,
  "unassigned_fields": 4,
  "total_plans": 35,
  "total_area": 25.5,
  "by_crop": [
    { "crop": "米", "count": 20, "area": 15.3 },
    { "crop": "トウモロコシ", "count": 8, "area": 5.2 }
  ]
}
```

**前年度コピー リクエスト**:
```json
// POST /api/plans/copy_from_previous_year/
{ "from_year": 2025, "to_year": 2026 }
```

**一括更新 リクエスト**:
```json
// POST /api/plans/bulk_update/
{ "field_ids": [1, 3, 5], "year": 2026, "crop": 2, "variety": 4 }
```

### 3.5 作物・品種マスタAPI

| メソッド | エンドポイント | 説明 |
|---|---|---|
| GET | `/api/plans/crops/` | 作物一覧 |
| POST | `/api/plans/crops/` | 作物追加 |
| GET | `/api/plans/varieties/` | 品種一覧 |
| POST | `/api/plans/varieties/` | 品種追加 |
| DELETE | `/api/plans/varieties/{id}/` | 品種削除 |

---

## 4. 画面仕様

### 4.1 圃場一覧画面

**URL**: `/fields`
**ソース**: `frontend/src/app/fields/page.tsx`

**機能**:
- 全圃場をカード形式で一覧表示
- グループ名でフィルタ（ドロップダウン）
- 各カードに: 圃場名、住所、面積(反)、地主名、共済紐付け数、中山間紐付け数
- 「新規作成」ボタン → `/fields/new` に遷移
- カードクリック → `/fields/{id}` に遷移

**データ取得**: `GET /api/fields/` → 全圃場をフロントで表示

**グループフィルタのロジック**: 全圃場の `group_name` からユニーク値を抽出し、ドロップダウンで選択。「すべて」選択時は全件表示。

### 4.2 圃場詳細画面

**URL**: `/fields/{id}`
**ソース**: `frontend/src/app/fields/[id]/page.tsx`

**機能**:
- 圃場の全情報を表示（名前、住所、面積、地主、グループ）
- インライン編集（各フィールドの編集ボタンでトグル）
- **共済マスタ紐付けセクション**: 紐付いている共済区画の一覧表示 + 紐付け追加/削除
- **中山間マスタ紐付けセクション**: 紐付いている中山間区画の一覧表示 + 紐付け追加/削除
- 紐付け追加はモーダル（`LinkModal`コンポーネント）で実施
- 圃場削除ボタン（確認ダイアログ付き）

**紐付け追加モーダル（LinkModal）**:
- ソース: `frontend/src/components/LinkModal.tsx`
- 未紐付けのマスタ区画を一覧表示
- チェックボックスで複数選択 → 一括追加
- 既に他の圃場に紐付いている区画も表示（紐付け先圃場名を表示）

**データフロー**:
```
圃場詳細表示: GET /api/fields/{id}/
紐付け候補取得: GET /api/kyosai-fields/ または /api/chusankan-fields/
紐付け追加: POST /api/fields/{id}/kyosai-links/ or /chusankan-links/
紐付け削除: DELETE /api/fields/{id}/kyosai-links/{kyosai_id}/
圃場更新: PATCH /api/fields/{id}/
圃場削除: DELETE /api/fields/{id}/
```

### 4.3 圃場新規作成画面

**URL**: `/fields/new`
**ソース**: `frontend/src/app/fields/new/page.tsx`

**入力フィールド**:
- 圃場名（必須）
- 住所（必須）
- 面積・反（必須）→ 入力時に自動でm2計算
- 地主名（必須）
- グループ名（任意）

**バリデーション**: フロント側で必須チェック。面積は数値チェック。

### 4.4 ダッシュボード画面

**URL**: `/dashboard`
**ソース**: `frontend/src/app/dashboard/page.tsx`

圃場管理に関連する表示:
- **全圃場数** / **作付け済み圃場数** / **未割当圃場数** のサマリーカード
- 作物別集計テーブル（面積・筆数）
- クイックアクセスボタン（作付け計画、圃場管理、帳票出力、データ取込）

### 4.5 作付け計画画面

**URL**: `/allocation`
**ソース**: `frontend/src/app/allocation/page.tsx`

圃場管理に関連する機能:
- 全圃場を表形式で表示（圃場名、面積、作物、品種、備考）
- **グループ名**: インライン編集（datalistで既存グループから選択 or 自由入力）
- **表示順**: カスタム順モードで↑↓ボタンで変更（PATCH /api/fields/{id}/ で保存）
- **ソート**: カスタム順 / グループ順 / 作付け順
- **検索・フィルタ**: 圃場名・住所テキスト検索、作物フィルタ、未割当のみ表示
- **チェックボックス一括操作**: 複数圃場を選択して作物・品種を一括設定
- **年度管理**: localStorageで年度保持、過去年度は参照モード（amber色表示）

---

## 5. インポート仕様

### 5.1 インポート画面

**URL**: `/import`
**ソース**: `frontend/src/app/import/page.tsx`

3種類のODSファイルをインポートする画面。**インポート順序が重要**:

```
1. 共済マスタ (水稲共済細目用.ods)     ← 先にインポート
2. 中山間マスタ (中山間.ods)           ← 先にインポート
3. 実圃場 (吉田農地台帳.ods)           ← 最後（M:N紐付けのため）
```

### 5.2 共済マスタインポート

**エンドポイント**: `POST /api/fields/import/kyosai/`
**ソース**: `backend/apps/fields/views.py:80-139`

**ODSカラム → DBフィールド マッピング**:

| ODSカラム名 | DBフィールド | 変換処理 |
|---|---|---|
| 耕地番号 | k_num | strip() |
| 分筆番号 | s_num | strip() |
| 地名 地番 | address | strip() |
| 漢字地名 | kanji_name | strip() |
| 本地面積 (m2) ※スペース有無両対応 | area | `float(値) × 100`（アール→m2変換） |

**重要**: ODSファイルの面積値は**アール(a)単位**。m2への変換で `×100` している。

**upsertロジック**: `(k_num, s_num)` でマッチングし、`update_or_create`。

### 5.3 中山間マスタインポート

**エンドポイント**: `POST /api/fields/import/chusankan/`
**ソース**: `backend/apps/fields/views.py:232-304`

**ODSカラム → DBフィールド マッピング**:

| ODSカラム名 | DBフィールド | 変換処理 |
|---|---|---|
| ID | c_id | strip() |
| 中山間 | chusankan_flag | safe_str() |
| 大字 | oaza | safe_str() |
| 字 | aza | safe_str() |
| 地番 | chiban | safe_str() |
| 枝番 | branch_num | safe_str() or None |
| 地目 | land_type | safe_str() or None |
| 農地面積 | area | safe_int() |
| 植栽面積 | planting_area | safe_int() |
| 作付け品目 | original_crop | safe_str() or None |
| 協定管理者 | manager | safe_str() or None |
| 所有者 | owner | safe_str() or None |
| 傾斜度 | slope | safe_str() or None |
| 基本金額 | base_amount | safe_int() |
| 超急傾斜加算額 | steep_slope_addition | safe_int() |
| スマート農業加算額 | smart_agri_addition | safe_int() |
| 交付金額 | payment_amount | safe_int() |

**スキップ条件**: c_idが空、または数字を含まない行はスキップ。

**upsertロジック**: `c_id` でマッチングし、`update_or_create`。

### 5.4 実圃場インポート

**エンドポイント**: `POST /api/fields/import/yoshida/`
**ソース**: `backend/apps/fields/views.py:142-229`

**ODSカラム → DBフィールド マッピング**:

| ODSカラム名 | DBフィールド | 変換処理 |
|---|---|---|
| 名称 | name | strip() |
| 住所 | address | strip() |
| 面積(反) | area_tan, area_m2 | area_m2 = area_tan × 1000 |
| 地主 | owner_name | strip() |
| 細目_耕地番号 | raw_kyosai_k_num | clean_int_str() |
| 細目_分筆番号 | raw_kyosai_s_num | clean_int_str() |
| 中山間_ID | raw_chusankan_id | clean_int_str(), カンマ区切り対応 |

**upsertロジック**: `name` でマッチングし、`update_or_create`。

**M:N紐付け処理（インポート時に自動実行）**:
1. `raw_kyosai_k_num` と `raw_kyosai_s_num` の両方がある場合 → 共済マスタの `(k_num, s_num)` で検索 → `field.kyosai_fields.add()`
2. `raw_chusankan_id` がある場合 → カンマ区切りで分割 → 各IDで中山間マスタを検索 → `field.chusankan_fields.add()`

**clean_int_str()関数**: NaN/空文字 → None、末尾 `.0` を除去。

---

## 6. エクスポート仕様

**エンドポイント**: `GET /api/fields/export/zip/`
**ソース**: `backend/apps/fields/views.py:307-380`

全データをCSV形式でZIPアーカイブとしてエクスポート。

**含まれるCSVファイル**:

| ファイル名 | 内容 | カラム |
|---|---|---|
| fields.csv | 実圃場 | id, name, address, area_tan, area_m2, owner_name, group_name, display_order |
| kyosai_fields.csv | 共済マスタ | id, k_num, s_num, address, kanji_name, area |
| chusankan_fields.csv | 中山間マスタ | id, c_id, chusankan_flag, oaza, aza, chiban, branch_num, land_type, area, planting_area, original_crop, manager, owner, slope, base_amount, steep_slope_addition, smart_agri_addition, payment_amount |
| plans.csv | 作付け計画 | id, field_id, field_name, year, crop_id, crop_name, variety_id, variety_name, notes |
| crops_varieties.csv | 作物・品種 | crop_id, crop_name, variety_id, variety_name |
| field_links.csv | M:N紐付け | field_id, field_name, link_type(kyosai/chusankan), linked_id |

---

## 7. 対応表(E-2)機能

圃場詳細画面における共済/中山間マスタとの紐付け管理機能。

### 紐付け追加フロー

1. 圃場詳細画面で「共済区画を追加」or「中山間区画を追加」ボタンをクリック
2. `LinkModal` が開く
3. 全マスタ区画が一覧表示される（既に紐付いている区画にはチェック済み表示）
4. 各区画に `linked_field_names` が表示され、どの実圃場に紐付いているか確認可能
5. チェックボックスで選択 → 「追加」ボタンで `POST /api/fields/{id}/kyosai-links/` or `/chusankan-links/`

### 紐付け削除フロー

1. 圃場詳細画面の紐付きリストで「×」ボタンをクリック
2. 確認ダイアログ
3. `DELETE /api/fields/{id}/kyosai-links/{kyosai_id}/` or `/chusankan-links/{chusankan_id}/`

---

## 8. PDF帳票との関係

圃場データは PDF帳票生成で以下のように使用される。

### 8.1 水稲共済細目書 PDF

**エンドポイント**: `GET /api/reports/kyosai/{year}/`
**ソース**: `backend/apps/reports/views.py:38-66`

**データ集約ロジック**:
```
共済マスタ区画ごとにループ:
  1. 共済区画に紐づく実圃場群を取得 (kyosai.fields.all())
  2. 各実圃場の当年度Planから作物・品種を集約
  3. 行データとして出力
```

### 8.2 中山間交付金申請書 PDF

**エンドポイント**: `GET /api/reports/chusankan/{year}/`
**ソース**: `backend/apps/reports/views.py:69-105`

**データ集約ロジック**:
```
中山間マスタ区画ごとにループ:
  1. 中山間区画に紐づく実圃場群を取得 (ch.fields.all())
  2. 各実圃場の当年度Planから作物・品種を集約
  3. 所在地 = 大字 + 字 + 地番 (+ 枝番)
  4. 行データとして出力
```

**共通の集約関数**: `_get_plan_info(related_fields, year)` (`backend/apps/reports/views.py:8-35`)

---

## 9. 作付け計画との関係

### Plan → Field の関係

- `Plan.field` は `Field` への FK（CASCADE）
- `unique_together = ['field', 'year']` により、1つの圃場に年度あたり1つの計画のみ
- Field削除時、関連するPlanも全て削除される（CASCADE）

### 作付け計画画面での圃場操作

作付け計画画面（`/allocation`）では以下の圃場関連操作が可能:

| 操作 | API呼び出し | 説明 |
|---|---|---|
| グループ名変更 | `PATCH /api/fields/{id}/` | `{ "group_name": "上エリア" }` |
| 表示順変更 | `PATCH /api/fields/{id}/` | `{ "display_order": 5 }` |

---

## 10. 設計判断と制約

### 10.1 絶対に変えてはいけない制約

1. **Field ↔ OfficialKyosaiField は M:N** — 決してFKに変更しない
2. **Field ↔ OfficialChusankanField は M:N** — 決してFKに変更しない
3. **Plan の (field, year) ユニーク制約** — 1圃場1年度1計画
4. **共済マスタの (k_num, s_num) ユニーク制約**
5. **中山間マスタの c_id ユニーク制約**

### 10.2 面積の扱い

| 用途 | 単位 | フィールド |
|---|---|---|
| DB保存 | m2 (整数) | `area_m2` |
| 画面表示 | 反 (小数) | `area_tan` |
| 変換式 | 1反 = 1000m2 | 実際は991.736m2だが1000で統一 |
| 共済ODS | アール(a) | インポート時に `×100` でm2変換 |

### 10.3 年度管理の設計方針

- **作付け計画画面**: `localStorage` で選択年度を保持（セッション跨ぎで記憶）
- **過去年度**: 参照モード（amber色表示、「現在年度に戻る」ボタン）
- **ダッシュボード/帳票**: 常に `new Date().getFullYear()` をデフォルト
- **Phase 2**: グローバル作業年度の導入予定

### 10.4 認証

- JWT認証（`rest_framework_simplejwt`）
- アクセストークン: 24時間有効
- リフレッシュトークン: 7日間有効
- フロントエンド: `localStorage` にトークン保存、`axios` interceptorで自動付与・リフレッシュ
- API設定: `DEFAULT_PERMISSION_CLASSES = IsAuthenticated`

---

## 11. ソースファイル索引

### バックエンド

| ファイル | 行数 | 内容 |
|---|---|---|
| `backend/apps/fields/models.py` | 79行 | Field, OfficialKyosaiField, OfficialChusankanField モデル定義 |
| `backend/apps/fields/views.py` | 381行 | ViewSet、インポート3種、エクスポート、M:Nリンク操作 |
| `backend/apps/fields/serializers.py` | 37行 | FieldSerializer（ネストあり）、マスタSerializer |
| `backend/apps/fields/urls.py` | 18行 | ルーティング定義 |
| `backend/apps/plans/models.py` | 44行 | Crop, Variety, Plan モデル定義 |
| `backend/apps/plans/views.py` | 133行 | PlanViewSet（CRUD、summary、copy、bulk_update） |
| `backend/apps/plans/serializers.py` | 37行 | Plan, Crop, Variety シリアライザ |
| `backend/apps/plans/urls.py` | 13行 | ルーティング定義 |
| `backend/apps/reports/views.py` | 106行 | PDF生成（共済・中山間） |
| `backend/apps/reports/urls.py` | 7行 | ルーティング定義 |
| `backend/keinasystem/urls.py` | 35行 | ルートURL設定（マスタルーター含む） |
| `backend/keinasystem/settings.py` | 151行 | Django設定（DB, JWT, CORS等） |

### フロントエンド

| ファイル | 内容 |
|---|---|
| `frontend/src/app/fields/page.tsx` | 圃場一覧画面 |
| `frontend/src/app/fields/[id]/page.tsx` | 圃場詳細画面（M:N紐付け管理含む） |
| `frontend/src/app/fields/new/page.tsx` | 圃場新規作成画面 |
| `frontend/src/app/allocation/page.tsx` | 作付け計画画面（圃場グループ・順序操作含む） |
| `frontend/src/app/dashboard/page.tsx` | ダッシュボード（圃場数サマリー） |
| `frontend/src/app/import/page.tsx` | データ取込画面（3種インポート） |
| `frontend/src/app/reports/page.tsx` | 帳票出力画面 |
| `frontend/src/components/Navbar.tsx` | ナビゲーションバー |
| `frontend/src/components/LinkModal.tsx` | M:N紐付け追加モーダル |
| `frontend/src/lib/api.ts` | Axiosインスタンス、JWT認証インターセプター |
| `frontend/src/types/index.ts` | TypeScript型定義（Field, Plan, Crop, Variety等） |

### TypeScript型定義

```typescript
// frontend/src/types/index.ts

interface Field {
  id: number;
  name: string;
  address: string;
  area_tan: string;        // DecimalFieldはstring型で来る
  area_m2: number;
  owner_name: string;
  group_name: string | null;
  display_order: number;
  kyosai_fields: OfficialKyosaiField[];
  chusankan_fields: OfficialChusankanField[];
}

interface OfficialKyosaiField {
  id: number;
  k_num: string;
  s_num: string;
  address: string;
  kanji_name: string;
  area: number;
  linked_field_names?: string[];
}

interface OfficialChusankanField {
  id: number;
  c_id: string;
  oaza: string;
  aza: string;
  chiban: string;
  area: number;
  payment_amount: number | null;
  linked_field_names?: string[];
}

interface Plan {
  id: number;
  field: number;
  field_name: string;
  year: number;
  crop: number;
  crop_name: string;
  variety: number;
  variety_name: string;
  notes: string | null;
}

interface Crop {
  id: number;
  name: string;
  varieties: Variety[];
}

interface Variety {
  id: number;
  crop: number;
  name: string;
}
```

---

## 更新履歴

- 2026-02-20: 初版作成（Phase 1 MVP完了時点の全機能を網羅）
