マスタードキュメント作成

This commit is contained in:
Akira
2026-02-21 12:26:11 +09:00
parent 9169018392
commit be50c41eef

View File

@@ -0,0 +1,754 @@
# マスタードキュメント - 圃場管理編
> **最終更新**: 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可 | インポート時の中山間IDraw値、カンマ区切り複数可 |
| 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` への FKCASCADE
- `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行 | PlanViewSetCRUD、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完了時点の全機能を網羅