マスタードキュメント作成
This commit is contained in:
754
document/10_マスタードキュメント_圃場管理編.md
Normal file
754
document/10_マスタードキュメント_圃場管理編.md
Normal 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可 | インポート時の中山間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完了時点の全機能を網羅)
|
||||
Reference in New Issue
Block a user