commit ed899fb97d694973caa7582fbfe4cb76deb2770c Author: Akira Date: Sun Feb 15 10:33:34 2026 +0900 初期仕様案 diff --git a/00_Gemini向け統合指示書.md b/00_Gemini向け統合指示書.md new file mode 100644 index 0000000..a0826c2 --- /dev/null +++ b/00_Gemini向け統合指示書.md @@ -0,0 +1,936 @@ +# Keina System - Gemini向け実装指示書 + +## 📋 このドキュメントについて + +これは、農業生産者向けの作付け計画管理システム「Keina System」の実装に必要な全情報を統合したドキュメントです。 + +**実装エージェント(Gemini/OpenCode)は、以下の順序でドキュメントを読み、理解してから実装を開始してください。** + +--- + +## 🎯 システムの全体像 + +### 目的 +年間の作付け計画を管理し、役場への申請書類(水稲共済細目書・中山間地域等直接支払交付金)を自動生成するシステム。 + +### ユーザー +- 65歳の農家(元プログラマー) +- シングルユーザー(マルチテナント不要) +- PCで登録・編集、スマホで参照 + +### 主要機能(Phase 1 / MVP) +1. 作付け計画の一覧表示・編集 +2. 水稲共済細目書のCSV出力 +3. 中山間交付金申請書のCSV出力 +4. 前年度作付けのコピー機能 +5. 圃場情報のスマホ参照 + +### 技術スタック +- **バックエンド**: Django 5.0 + Django REST Framework + GeoDjango +- **フロントエンド**: Next.js 14 (App Router) + Tailwind CSS +- **データベース**: PostgreSQL 16 + PostGIS 3.4 +- **インフラ**: Docker Compose + +--- + +## 📚 必読ドキュメント(この順に読むこと) + +実装前に、以下のドキュメントを**必ず全て読んでください**。各ドキュメントには実装に必要な重要な情報が含まれています。 + +### 1. プロダクトビジョン.md +- システムの目的、ユーザー像、成功の定義 +- なぜこのシステムを作るのか、何を目指すのか +- **👉 読むべき理由**: ゴールを理解しないと、適切な実装判断ができない + +### 2. ユーザーストーリー.md +- 具体的な利用シーン(優先度付き) +- 受け入れ基準、実装すべき機能の詳細 +- **👉 読むべき理由**: 何を作るべきか、どこまで作るべきかが明確になる + +### 3. データ仕様書.md +- 3種類のデータ(実圃場、共済マスタ、中山間マスタ)の関係 +- 紐付けロジック(M:1関係)の詳細 +- 申請書CSV出力のアルゴリズム +- **👉 読むべき理由**: データモデルの設計ミスは後から修正困難 + +### 4. 画面設計書.md +- 全画面のワイヤーフレーム +- UI共通仕様(カラー、フォント、レスポンシブ) +- **👉 読むべき理由**: UIの実装イメージが具体的につかめる + +### 5. 実装優先順位.md +- 10日間の実装スケジュール +- 技術スタックの詳細、潜在的な課題と対策 +- **👉 読むべき理由**: 何から作るか、どう作るかの指針 + +--- + +## 🚀 実装の進め方(ステップバイステップ) + +### Step 0: 事前準備(必須) +1. 上記5つのドキュメントを**全て読む** +2. 不明点があれば、実装開始前に質問する +3. 実データ(`吉田農地台帳.ods`, `水稲共済細目用.ods`, `中山間.ods`)を確認 + +### Step 1: 環境構築(Day 1) +```bash +# プロジェクト構造 +keinasystem/ +├── docker-compose.yml +├── backend/ +│ ├── Dockerfile +│ ├── requirements.txt +│ ├── manage.py +│ └── keinasystem/ +│ ├── settings.py +│ ├── urls.py +│ └── apps/ +│ ├── fields/ # 圃場管理 +│ ├── plans/ # 作付け計画 +│ └── reports/ # 申請書出力 +└── frontend/ + ├── Dockerfile + ├── package.json + ├── app/ + │ ├── login/ + │ ├── dashboard/ + │ ├── allocation/ # 作付け計画編集 + │ └── reports/ # 申請書ダウンロード + └── lib/ + ├── api.ts # API呼び出し + └── types.ts # 型定義 +``` + +#### docker-compose.yml +```yaml +version: '3.8' + +services: + db: + image: postgis/postgis:16-3.4 + environment: + POSTGRES_DB: keina_db + POSTGRES_USER: keina_user + POSTGRES_PASSWORD: ${DB_PASSWORD} + volumes: + - postgres_data:/var/lib/postgresql/data + ports: + - "5432:5432" + + backend: + build: ./backend + command: python manage.py runserver 0.0.0.0:8000 + volumes: + - ./backend:/app + ports: + - "8000:8000" + environment: + DATABASE_URL: postgis://keina_user:${DB_PASSWORD}@db:5432/keina_db + SECRET_KEY: ${SECRET_KEY} + depends_on: + - db + + frontend: + build: ./frontend + command: npm run dev + volumes: + - ./frontend:/app + - /app/node_modules + ports: + - "3000:3000" + environment: + NEXT_PUBLIC_API_URL: http://localhost:8000 + +volumes: + postgres_data: +``` + +### Step 2: Django セットアップ(Day 1-2) + +#### backend/requirements.txt +```txt +Django==5.0 +djangorestframework==3.14 +django-cors-headers==4.3 +psycopg2-binary==2.9 +djoser==2.2 +djangorestframework-simplejwt==5.3 +pandas==2.1 +odfpy==1.4 +WeasyPrint==60.1 +``` + +#### backend/keinasystem/settings.py(重要な設定のみ) +```python +INSTALLED_APPS = [ + # Django標準 + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'django.contrib.gis', # GeoDjango + + # サードパーティ + 'rest_framework', + 'rest_framework_simplejwt', + 'djoser', + 'corsheaders', + + # 自作アプリ + 'apps.fields', + 'apps.plans', + 'apps.reports', +] + +# PostGIS設定 +DATABASES = { + 'default': { + 'ENGINE': 'django.contrib.gis.db.backends.postgis', + 'NAME': 'keina_db', + 'USER': 'keina_user', + 'PASSWORD': os.getenv('DB_PASSWORD'), + 'HOST': 'db', + 'PORT': '5432', + } +} + +# REST Framework設定 +REST_FRAMEWORK = { + 'DEFAULT_AUTHENTICATION_CLASSES': [ + 'rest_framework_simplejwt.authentication.JWTAuthentication', + ], + 'DEFAULT_PERMISSION_CLASSES': [ + 'rest_framework.permissions.IsAuthenticated', + ], +} + +# JWT設定 +from datetime import timedelta +SIMPLE_JWT = { + 'ACCESS_TOKEN_LIFETIME': timedelta(days=1), + 'REFRESH_TOKEN_LIFETIME': timedelta(days=7), +} + +# CORS設定 +CORS_ALLOWED_ORIGINS = [ + "http://localhost:3000", +] +``` + +### Step 3: データモデル実装(Day 3) + +#### apps/fields/models.py +```python +from django.contrib.gis.db import models + +class OfficialKyosaiField(models.Model): + """共済マスタ(水稲共済細目用.ods)""" + k_num = models.IntegerField("耕地番号") + s_num = models.IntegerField("分筆番号") + address = models.CharField("地名地番", max_length=200) + kanji_name = models.CharField("漢字地名", max_length=200) + area = models.FloatField("本地面積(m2)") + + class Meta: + unique_together = [['k_num', 's_num']] + ordering = ['k_num', 's_num'] + +class OfficialChusankanField(models.Model): + """中山間マスタ(中山間.ods)""" + c_id = models.IntegerField("ID", unique=True) + oaza = models.CharField("大字", max_length=100) + aza = models.CharField("字", max_length=100) + chiban = models.IntegerField("地番") + area = models.IntegerField("農地面積(m2)") + payment_amount = models.IntegerField("交付金額", null=True, blank=True) + + class Meta: + ordering = ['c_id'] + +class Field(models.Model): + """実圃場(吉田農地台帳.ods)""" + name = models.CharField("名称", max_length=100) + address = models.CharField("住所", max_length=200) + area_tan = models.FloatField("面積(反)") + area_m2 = models.IntegerField("面積(m2)") # area_tan * 1000 + owner_name = models.CharField("地主", max_length=100) + + # 紐付けキー(raw値) + raw_kyosai_k_num = models.IntegerField("細目_耕地番号") + raw_kyosai_s_num = models.IntegerField("細目_分筆番号") + raw_chusankan_id = models.IntegerField("中山間_ID", null=True, blank=True) + + # 外部キー(紐付け済み) + kyosai_field = models.ForeignKey( + OfficialKyosaiField, + on_delete=models.SET_NULL, + null=True, + related_name='fields' + ) + chusankan_field = models.ForeignKey( + OfficialChusankanField, + on_delete=models.SET_NULL, + null=True, + related_name='fields' + ) + + # 地理情報(Phase 1では使用しないが、構造だけ準備) + location = models.PointField(null=True, blank=True) + + class Meta: + ordering = ['name'] +``` + +#### apps/plans/models.py +```python +from django.db import models +from apps.fields.models import Field + +class Crop(models.Model): + """作物マスタ""" + name = models.CharField("作物名", max_length=50, unique=True) + is_planting = models.BooleanField("作付けする", default=True) + + class Meta: + ordering = ['name'] + +class Variety(models.Model): + """品種マスタ""" + crop = models.ForeignKey(Crop, on_delete=models.CASCADE, related_name='varieties') + name = models.CharField("品種名", max_length=100) + + class Meta: + ordering = ['crop', 'name'] + +class Plan(models.Model): + """作付け計画""" + field = models.ForeignKey(Field, on_delete=models.CASCADE, related_name='plans') + year = models.IntegerField("年度") + crop = models.ForeignKey(Crop, on_delete=models.PROTECT, null=True, blank=True) + variety = models.ForeignKey(Variety, on_delete=models.SET_NULL, null=True, blank=True) + notes = models.TextField("備考", blank=True) + + class Meta: + unique_together = [['field', 'year']] + ordering = ['year', 'field'] +``` + +### Step 4: インポート機能(Day 4) + +#### apps/fields/views.py(インポートAPI) +```python +from rest_framework.decorators import api_view +from rest_framework.response import Response +import pandas as pd + +@api_view(['POST']) +def import_kyosai_master(request): + """共済マスタのインポート""" + file = request.FILES['file'] + df = pd.read_excel(file, engine='odf') + + for _, row in df.iterrows(): + OfficialKyosaiField.objects.update_or_create( + k_num=row['耕地番号'], + s_num=row['分筆番号'], + defaults={ + 'address': row['地名 地番'], + 'kanji_name': row['漢字地名'], + 'area': row['本地面積 (m2)'], + } + ) + + return Response({'status': 'success', 'imported': len(df)}) + +@api_view(['POST']) +def import_yoshida_fields(request): + """吉田農地台帳のインポート(紐付け処理含む)""" + file = request.FILES['file'] + df = pd.read_excel(file, engine='odf') + + for _, row in df.iterrows(): + # 共済マスタとの紐付け + try: + kyosai = OfficialKyosaiField.objects.get( + k_num=row['細目_耕地番号'], + s_num=row['細目_分筆番号'] + ) + except OfficialKyosaiField.DoesNotExist: + kyosai = None + + # 中山間マスタとの紐付け + chusankan = None + if pd.notna(row['中山間_ID']): + try: + chusankan = OfficialChusankanField.objects.get( + c_id=int(row['中山間_ID']) + ) + except OfficialChusankanField.DoesNotExist: + pass + + # 実圃場を作成 + Field.objects.update_or_create( + name=row['名称'], + defaults={ + 'address': row['住所'], + 'area_tan': row['面積(反)'], + 'area_m2': int(row['面積(反)'] * 1000), + 'owner_name': row['地主'], + 'raw_kyosai_k_num': row['細目_耕地番号'], + 'raw_kyosai_s_num': row['細目_分筆番号'], + 'raw_chusankan_id': int(row['中山間_ID']) if pd.notna(row['中山間_ID']) else None, + 'kyosai_field': kyosai, + 'chusankan_field': chusankan, + } + ) + + return Response({'status': 'success', 'imported': len(df)}) +``` + +### Step 5: 作付け計画API(Day 5) + +#### apps/plans/views.py +```python +from rest_framework import viewsets +from rest_framework.decorators import action +from rest_framework.response import Response +from .models import Plan +from .serializers import PlanSerializer + +class PlanViewSet(viewsets.ModelViewSet): + queryset = Plan.objects.all() + serializer_class = PlanSerializer + + def get_queryset(self): + queryset = super().get_queryset() + year = self.request.query_params.get('year') + if year: + queryset = queryset.filter(year=year) + return queryset + + @action(detail=False, methods=['post']) + def bulk_update(self, request): + """一括更新""" + field_ids = request.data.get('field_ids', []) + crop_id = request.data.get('crop_id') + variety_id = request.data.get('variety_id') + year = request.data.get('year') + + for field_id in field_ids: + Plan.objects.update_or_create( + field_id=field_id, + year=year, + defaults={ + 'crop_id': crop_id, + 'variety_id': variety_id, + } + ) + + return Response({'status': 'success', 'updated': len(field_ids)}) + + @action(detail=False, methods=['post']) + def copy_from_previous_year(self, request): + """前年度コピー""" + source_year = request.data.get('source_year') + target_year = request.data.get('target_year') + + plans = Plan.objects.filter(year=source_year) + + for plan in plans: + Plan.objects.update_or_create( + field=plan.field, + year=target_year, + defaults={ + 'crop': plan.crop, + 'variety': plan.variety, + 'notes': plan.notes, + } + ) + + return Response({'status': 'success', 'copied': len(plans)}) +``` + +### Step 6: 申請書PDF生成(Day 7) + +#### apps/reports/views.py +```python +from rest_framework.decorators import api_view +from django.http import HttpResponse +from django.template.loader import render_to_string +from weasyprint import HTML +from apps.fields.models import OfficialKyosaiField, OfficialChusankanField +from apps.plans.models import Plan + +@api_view(['GET']) +def generate_kyosai_pdf(request): + """水稲共済細目書PDF""" + year = int(request.query_params.get('year')) + + # 1. データ集約 + output_rows = [] + for kyosai in OfficialKyosaiField.objects.all().order_by('k_num', 's_num'): + # この共済区画に紐づく実圃場を取得 + fields = kyosai.fields.all() + + # 各実圃場の作付け計画を取得 + plans = Plan.objects.filter( + field__in=fields, + year=year + ).select_related('crop', 'variety') + + # 作物名と品種を集約 + crops = sorted(list(set([p.crop.name for p in plans if p.crop]))) + varieties = sorted(list(set([p.variety.name for p in plans if p.variety]))) + + output_rows.append({ + 'k_num': kyosai.k_num, + 's_num': kyosai.s_num, + 'address': kyosai.address, + 'kanji_name': kyosai.kanji_name, + 'area': kyosai.area, + 'crops': ','.join(crops) if crops else '未設定', + 'varieties': ','.join(varieties) if varieties else '', + 'note': f'{len(fields)}筆合算' if len(fields) > 1 else '' + }) + + # 2. HTMLテンプレートで表を生成 + html = render_to_string('reports/kyosai_template.html', { + 'year': year, + 'rows': output_rows + }) + + # 3. HTML → PDF変換 + pdf = HTML(string=html).write_pdf() + + response = HttpResponse(pdf, content_type='application/pdf') + response['Content-Disposition'] = f'attachment; filename="kyosai_{year}.pdf"' + return response + +@api_view(['GET']) +def generate_chusankan_pdf(request): + """中山間交付金PDF""" + year = int(request.query_params.get('year')) + + # データ集約(共済と同様) + output_rows = [] + for chusankan in OfficialChusankanField.objects.all().order_by('c_id'): + fields = chusankan.fields.all() + plans = Plan.objects.filter(field__in=fields, year=year).select_related('crop', 'variety') + + crops = sorted(list(set([p.crop.name for p in plans if p.crop]))) + varieties = sorted(list(set([p.variety.name for p in plans if p.variety]))) + + output_rows.append({ + 'c_id': chusankan.c_id, + 'oaza': chusankan.oaza, + 'aza': chusankan.aza, + 'chiban': chusankan.chiban, + 'area': chusankan.area, + 'crops': ','.join(crops) if crops else '未設定', + 'varieties': ','.join(varieties) if varieties else '', + 'note': f'{len(fields)}筆合算' if len(fields) > 1 else '' + }) + + html = render_to_string('reports/chusankan_template.html', { + 'year': year, + 'rows': output_rows + }) + + pdf = HTML(string=html).write_pdf() + + response = HttpResponse(pdf, content_type='application/pdf') + response['Content-Disposition'] = f'attachment; filename="chusankan_{year}.pdf"' + return response +``` + +#### apps/reports/templates/reports/kyosai_template.html +```html + + + + + + + +

水稲共済細目書({{ year }}年度)

+ + + + + + + + + + + + + + + {% for row in rows %} + + + + + + + + + + + {% endfor %} + +
耕地番号分筆番号地名地番漢字地名本地面積
(m2)
作付品目品種備考
{{ row.k_num }}{{ row.s_num }}{{ row.address }}{{ row.kanji_name }}{{ row.area }}{{ row.crops }}{{ row.varieties }}{{ row.note }}
+ + +``` + +#### apps/reports/templates/reports/chusankan_template.html +```html + + + + + + + +

中山間地域等直接支払交付金({{ year }}年度)

+ + + + + + + + + + + + + + + {% for row in rows %} + + + + + + + + + + + {% endfor %} + +
ID大字地番農地面積
(m2)
作付品目品種備考
{{ row.c_id }}{{ row.oaza }}{{ row.aza }}{{ row.chiban }}{{ row.area }}{{ row.crops }}{{ row.varieties }}{{ row.note }}
+ + +``` + +#### apps/reports/urls.py +```python +from django.urls import path +from . import views + +urlpatterns = [ + path('kyosai/', views.generate_kyosai_pdf, name='kyosai_pdf'), + path('chusankan/', views.generate_chusankan_pdf, name='chusankan_pdf'), +] +``` + +### Step 7: フロントエンド実装(Day 6, 8-9) + +#### frontend/app/reports/page.tsx(申請書ダウンロード画面) +```tsx +'use client' + +import { useState } from 'react' + +export default function ReportsPage() { + const [year, setYear] = useState(2025) + + const downloadPDF = async (type: 'kyosai' | 'chusankan') => { + const endpoint = type === 'kyosai' + ? `/api/reports/kyosai/?year=${year}` + : `/api/reports/chusankan/?year=${year}` + + try { + const response = await fetch(`http://localhost:8000${endpoint}`, { + headers: { + 'Authorization': `JWT ${localStorage.getItem('accessToken')}` + } + }) + + const blob = await response.blob() + const url = window.URL.createObjectURL(blob) + const a = document.createElement('a') + a.href = url + a.download = type === 'kyosai' + ? `水稲共済細目書_${year}年度.pdf` + : `中山間交付金_${year}年度.pdf` + document.body.appendChild(a) + a.click() + window.URL.revokeObjectURL(url) + document.body.removeChild(a) + } catch (error) { + console.error('PDF download failed:', error) + alert('PDFのダウンロードに失敗しました') + } + } + + const previewPDF = (type: 'kyosai' | 'chusankan') => { + const endpoint = type === 'kyosai' + ? `/api/reports/kyosai/?year=${year}` + : `/api/reports/chusankan/?year=${year}` + + const url = `http://localhost:8000${endpoint}` + window.open(url, '_blank') + } + + return ( +
+

申請書ダウンロード

+ +
+ + +
+ +
+ {/* 水稲共済細目書 */} +
+

📄 水稲共済細目書

+

提出時期: 2月・5月(年2回)

+

区画数: 31区画

+ +
+ + +
+
+ + {/* 中山間交付金 */} +
+

📄 中山間地域等直接支払交付金

+

提出時期: 5月(年1回)

+

区画数: 71区画

+ +
+ + +
+
+
+
+ ) +} +``` + +--- + +## ⚠️ 重要な実装上の注意点 + +### 1. データベース設計 +- **面積単位**: DB内部は全て `m2` で保存、表示時に `反` に変換 +- **紐付けキー**: `raw_*` フィールドと外部キー `*_field` の両方を持つ +- **ユニーク制約**: `(field, year)` で作付け計画は1つまで + +### 2. 申請書CSV生成 +- **共済マスタをベースにループ**: 実圃場ベースではない +- **作物の名寄せ**: Pythonのセットで重複排除 +- **未割当の扱い**: 「未設定」として出力 + +### 3. UI/UX +- **未割当の強調**: 赤または黄色の背景色 +- **スマホ対応**: 文字サイズ16px以上、タップ領域44px以上 +- **検索のリアルタイム性**: `useState` + `filter` で実装 + +### 4. 認証 +- **JWT認証**: アクセストークン(1日)+ リフレッシュトークン(7日) +- **シンプルな実装**: パスワードリセットはPhase 2 + +### 5. パフォーマンス +- **Phase 1では最適化不要**: 圃場数39筆、共済31区画、中山間71区画 → 十分軽い +- **Phase 2で検討**: 栽培履歴が増えたらページネーション + +--- + +## 📝 実装チェックリスト + +実装完了時に、以下を全て確認してください: + +### 環境構築 +- [ ] `docker-compose up` でコンテナが全て起動する +- [ ] PostgreSQL + PostGIS が正常に動作する +- [ ] Django の `makemigrations` / `migrate` が成功する +- [ ] Next.js の `npm run dev` が成功する + +### バックエンド +- [ ] `/admin` でDjango管理画面にアクセスできる +- [ ] `/api/auth/jwt/create/` でJWTトークンを取得できる +- [ ] `/api/fields/` で圃場一覧を取得できる +- [ ] `/api/plans/?year=2025` で作付け計画を取得できる +- [ ] `/api/reports/kyosai/?year=2025` でPDFをダウンロードできる +- [ ] `/api/reports/chusankan/?year=2025` でPDFをダウンロードできる + +### フロントエンド +- [ ] `/login` でログインできる +- [ ] `/allocation` で作付け計画一覧を表示できる +- [ ] 作付け計画を編集できる +- [ ] `/reports` で申請書をダウンロードできる +- [ ] PDFをプレビュー表示できる(新しいタブ) +- [ ] スマホで見やすい(Chrome DevToolsで確認) + +### データ整合性 +- [ ] 共済PDFの耕地番号が正しい +- [ ] 中山間PDFのIDが正しい +- [ ] 作物の名寄せが正しい +- [ ] 未割当の圃場が適切に扱われる +- [ ] PDFの表レイアウトが整っている +- [ ] PDFをA4用紙に印刷して読める(フォントサイズ、余白) + +--- + +## 🆘 トラブルシューティング + +### PostGISが動かない +```python +# settings.py に以下を追加 +GDAL_LIBRARY_PATH = '/usr/lib/libgdal.so' +GEOS_LIBRARY_PATH = '/usr/lib/libgeos_c.so' +``` + +### ODSファイルが読めない +```bash +pip install odfpy --break-system-packages +``` + +### WeasyPrintのインストールエラー +```bash +# Ubuntu/Debianの場合、システムライブラリが必要 +apt-get install python3-cffi python3-brotli libpango-1.0-0 libpangoft2-1.0-0 +pip install WeasyPrint --break-system-packages +``` + +### PDFの日本語が表示されない +```python +# HTMLテンプレートのCSSに日本語フォントを明示 +body { + font-family: "MS Gothic", "Yu Gothic", "Hiragino Sans", sans-serif; +} +``` + +### CORSエラー +```python +# settings.py +CORS_ALLOWED_ORIGINS = [ + "http://localhost:3000", +] +``` + +### マイグレーションエラー +```bash +docker-compose exec backend python manage.py makemigrations +docker-compose exec backend python manage.py migrate +``` + +--- + +## 🎉 実装完了の定義 + +以下が全て完了したら、Phase 1は実装完了とする: + +1. ✅ ログインできる +2. ✅ 作付け計画を編集できる +3. ✅ 水稲共済PDFをダウンロードできる +4. ✅ 中山間PDFをダウンロードできる +5. ✅ PDFをプレビュー表示できる +6. ✅ PDFをA4用紙に印刷してそのまま提出できる品質 +7. ✅ 前年度コピーができる +8. ✅ スマホで圃場情報を見られる +9. ✅ 実データ(3つのODSファイル)をインポートできる + +--- + +## 📞 質問・不明点がある場合 + +実装中に疑問が生じた場合、以下を確認してください: + +1. **データ仕様書.md**: データの紐付けロジックが不明な場合 +2. **画面設計書.md**: UIの詳細が不明な場合 +3. **ユーザーストーリー.md**: 機能の目的・受け入れ基準が不明な場合 + +それでも解決しない場合は、**実装を止めて質問してください**。 + +--- + +**Good luck with the implementation! 🚀** diff --git a/01_プロダクトビジョン.md b/01_プロダクトビジョン.md new file mode 100644 index 0000000..5e942e8 --- /dev/null +++ b/01_プロダクトビジョン.md @@ -0,0 +1,156 @@ +# プロダクトビジョン + +## 🎯 システムの目的 + +**「作付け計画を起点とした農業経営データの一元管理」** + +このシステムは、年間の作付け計画を中心に、以下の3つの課題を解決する: + +1. **申請書類の作成負担を減らす** + - 水稲共済細目書(年2回: 2月・5月) + - 中山間地域等直接支払交付金(年1回: 5月) + - これらの申請に必要なデータを、作付け計画から自動生成 + +2. **実圃場と申請区画のずれを管理する** + - 実際に作業する圃場(39筆)と、申請書上の区画(共済31区画、中山間71区画)が異なる + - 複数の実圃場が1つの申請区画に紐づく関係(M:1)を明示的に管理 + - 紐付けは半自動化するが、手動修正も可能にする + +3. **将来の拡張を見据えた設計** + - Phase 2: 栽培履歴(播種日、農薬・肥料の散布記録) + - Phase 3: 資材計画(種苗・肥料・農薬の必要量計算) + - Phase 4: 収穫管理・販売管理との連携 + +--- + +## 👤 ユーザー像 + +**主要ユーザー(Sole User):** +- 65歳の農家(元プログラマー、50歳まで従事) +- ITリテラシー: 高い(自分でシステムを設計・実装できるレベル) +- 経営規模: 39筆の圃場を管理 + +**利用デバイス:** +- 🖥️ **PC**: 作付け計画の登録・編集、申請書のダウンロード(メイン操作) +- 📱 **スマホ/タブレット**: 圃場での参照(品種確認、面積確認、将来的には栽培履歴) + +**利用シーズン:** +- **11月~3月**: 作付け計画の策定・修正(前年度コピー→微調整) +- **2月**: 水稲共済細目書の提出(1回目) +- **5月**: 水稲共済細目書(2回目)+中山間交付金の申請 +- **通年**: スマホでの現場参照 + +--- + +## 📊 現状の課題とシステムによる解決 + +| 課題 | 現状(Before) | システム導入後(After) | +|------|---------------|----------------------| +| 申請書作成 | 紙の台帳から手作業で転記・集計 | ボタン1つでPDFダウンロード→印刷 | +| 圃場と申請区画の対応 | Excelで手動管理、照合が大変 | 自動紐付け+UI上で視覚的に確認・修正 | +| 前年度データの再利用 | 前年のExcelをコピー→手作業で修正 | 年度コピー機能で一括複製 | +| 作物の変更履歴 | 紙のメモ、記憶頼み | 過去年度の作付け計画を参照可能 | +| 現場での情報確認 | 家に戻って紙の台帳を確認 | スマホでその場で品種・面積を確認 | + +--- + +## ✅ 成功の定義(KPI) + +**Phase 1(MVP)の成功指標:** + +1. **申請書作成時間の短縮** + - 水稲共済: 手作業2時間 → システム5分(96%削減) + - 中山間: 手作業1時間 → システム3分(95%削減) + +2. **データの正確性向上** + - 転記ミスゼロ(自動集計のため) + - 圃場と申請区画の対応ミスゼロ(UIで視覚的に確認) + +3. **使いやすさ** + - 作付け計画の登録・修正が、PCで10分以内に完了 + - スマホでの圃場情報参照が、3タップ以内で完了 + - PDFを印刷してそのまま提出できる品質(レイアウト調整不要) + +**Phase 2以降の展望:** +- 栽培履歴の記録により、GAP認証の取得が可能に +- 資材計画の自動化により、発注漏れ・過剰在庫を削減 +- 収穫実績と計画の比較により、翌年の計画精度が向上 + +--- + +## 🔐 非機能要件 + +**シンプルさ最優先:** +- シングルユーザー(マルチテナント不要) +- 認証は最小限(メール+パスワード) +- 複雑な権限管理は不要 + +**レスポンシブ対応:** +- PC: 作付け計画の編集、申請書ダウンロード +- スマホ/タブレット: 参照メイン(将来的には簡易な記録入力も) + +**データの永続性:** +- 最低5年分のデータを保持(補助金の監査対応) +- バックアップ機能(CSV/Excelでのエクスポート) + +**パフォーマンス:** +- 圃場一覧の表示: 1秒以内 +- 申請書CSVの生成: 3秒以内 +- スマホでの圃場詳細表示: 2秒以内 + +--- + +## 🚫 やらないこと(Non-Goals) + +**Phase 1では以下は含めない:** +- マルチユーザー対応(将来的にも不要の可能性高) +- 地図上での圃場描画・編集(GeoJSON等は後回し) +- 自動ジオコーディング(住所→座標変換は手動でOK) +- リアルタイム同期(オフライン対応は不要) +- モバイルアプリ(PWAで十分) + +--- + +## 🎨 デザイン原則 + +1. **シンプル・イズ・ベスト** + - 1画面1機能を徹底 + - 複雑なUIコンポーネントは避ける(ドラッグ&ドロップ、カレンダーなど) + +2. **情報の優先順位を明確に** + - 最もよく使う情報を最も目立つ位置に + - 圃場一覧では「名称」「作付け作物」「面積」を最優先表示 + +3. **エラーを起こしにくい設計** + - 入力必須項目は最小限に + - 選択式(ドロップダウン)を優先、自由入力は最小限 + +4. **スマホファースト(参照時)** + - 文字サイズ: 最低16px + - タップ領域: 最低44px×44px + - 横スクロールは避ける + +5. **既存データを尊重** + - 役場データ(共済・中山間)の面積不整合は「そういうもの」として扱う + - ユーザーの運用を変えさせない(紙の台帳と同じ感覚で使える) + +--- + +## 📅 開発フェーズ + +**Phase 1(MVP): 2025年2月まで** +- 作付け計画の登録・編集 +- 申請書(水稲共済・中山間)のCSV出力 +- 圃場一覧の参照(PC/スマホ) + +**Phase 2: 2025年3月~** +- 栽培履歴の記録(播種日、農薬散布など) +- 作業予定のカレンダー表示 + +**Phase 3: 2025年度中** +- 資材計画(種苗・肥料・農薬の必要量計算) +- 収穫記録 + +**Phase 4: 将来** +- お米販売システムとの連携(API経由) +- スマート農業機器との連携(センサーデータ取込) diff --git a/02_ユーザーストーリー.md b/02_ユーザーストーリー.md new file mode 100644 index 0000000..ae8d049 --- /dev/null +++ b/02_ユーザーストーリー.md @@ -0,0 +1,299 @@ +# ユーザーストーリー + +## 📖 ストーリー記法 + +``` +【優先度】タイトル +As a ユーザー(役割) +I want ◯◯したい +So that △△できる(目的・価値) + +【受け入れ基準】 +- [ ] 条件1 +- [ ] 条件2 +``` + +--- + +## 🔴 Phase 1(MVP)- 必須機能 + +### P1-1: 作付け計画の一覧表示 + +**As a** 農家(システムの唯一のユーザー) +**I want** 全ての圃場と、それぞれに何を作付けしたかを一覧で見たい +**So that** 今年の作付け状況を俯瞰でき、未割当の圃場を見つけられる + +**【受け入れ基準】** +- [ ] 全39筆の圃場が一覧表示される +- [ ] 各圃場に「名称」「面積」「今年の作付け作物」が表示される +- [ ] 作付け未設定の圃場は警告色(赤/黄)でハイライトされる +- [ ] 年度を切り替えられる(2024年度、2025年度など) +- [ ] PC・スマホ両方で見やすいレイアウト + +**【UIイメージ】** +``` +┌────────────────────────────────┐ +│ 📅 2025年度 作付け計画 │ +├────────────────────────────────┤ +│ 🔍 検索: [___________] 🔽絞込 │ +├────────────────────────────────┤ +│┌────┬──────┬────┬──────────┐│ +││名称 │面積 │作付 │操作 ││ +│├────┼──────┼────┼──────────┤│ +││田A │1.2反 │米 │ [編集] ││ +││田B │0.5反 │❗未設定│ [割当] ││ +│└────┴──────┴────┴──────────┘│ +└────────────────────────────────┘ +``` + +--- + +### P1-2: 圃場への作物割当 + +**As a** 農家 +**I want** 各圃場に作物を割り当てたい +**So that** 今年の作付け計画を記録できる + +**【受け入れ基準】** +- [ ] 圃場を選択して「作物」を設定できる +- [ ] 作物は以下から選択: + - 作付けしない: 休耕、緑肥、景観作物、その他野菜 + - 作付けする: 米(品種選択)、トウモロコシ、エンドウ、野菜 +- [ ] 品種も選択できる(例: 米 → にこまる、たちはるか、たちはるか特栽) +- [ ] 複数の圃場を一括選択して、同じ作物を割り当てられる +- [ ] 割当後、一覧画面に即座に反映される + +**【作物マスタ】** +``` +作付けしない: + - 休耕 + - 緑肥 + - 景観作物 + - その他野菜 + +作付けする: + - 米 + └ 品種: にこまる、たちはるか、たちはるか(特栽) + - トウモロコシ + └ 品種: (毎年変わる→自由入力) + - エンドウ + └ 品種: 久留米豊 + - 野菜 + └ 品種: (増減あり→自由入力) +``` + +--- + +### P1-3: 水稲共済細目書のPDF出力 + +**As a** 農家 +**I want** 水稲共済細目書に必要なデータをPDFでダウンロードしたい +**So that** 2月と5月の申請時に、印刷してそのまま役場に提出できる + +**【受け入れ基準】** +- [ ] 年度を指定してPDFをダウンロードできる +- [ ] PDFは表形式で、以下の列を含む: + ``` + 耕地番号 | 分筆番号 | 地名地番 | 漢字地名 | 本地面積(m2) | 作付品目 | 品種 | 備考 + ``` +- [ ] A4サイズ、縦向き、見やすいフォントサイズ(10pt以上) +- [ ] ヘッダーに「水稲共済細目書(2025年度)」などのタイトル +- [ ] ページ番号(複数ページになる場合) +- [ ] 共済マスタ(31区画)をベースに、紐づく実圃場の作付け情報を集約 +- [ ] 複数の実圃場が1つの共済区画に紐づく場合、作物をカンマ区切りで列挙(例: "米,野菜") +- [ ] 作付け未設定の共済区画も出力(空欄または「未設定」) +- [ ] ダウンロード前にプレビュー表示できる + +**【集計ロジック】** +1. 共済マスタ(`水稲共済細目用.ods`の31区画)をループ +2. 各共済区画に紐づく実圃場を取得(`吉田農地台帳`の`細目_耕地番号`/`細目_分筆番号`で結合) +3. 紐づく実圃場の作付け情報を集約(作物名をユニーク化してカンマ区切り) +4. HTMLテンプレートで表を生成 → PDF変換 + +--- + +### P1-4: 中山間交付金申請のPDF出力 + +**As a** 農家 +**I want** 中山間地域等直接支払交付金の申請に必要なデータをPDFでダウンロードしたい +**So that** 5月の申請時に、印刷してそのまま役場に提出できる + +**【受け入れ基準】** +- [ ] 年度を指定してPDFをダウンロードできる +- [ ] PDFは表形式で、以下の列を含む: + ``` + ID | 大字 | 字 | 地番 | 農地面積(m2) | 作付品目 | 品種 | 備考 + ``` +- [ ] A4サイズ、縦向き、見やすいフォントサイズ(10pt以上) +- [ ] ヘッダーに「中山間地域等直接支払交付金(2025年度)」などのタイトル +- [ ] 中山間マスタ(71区画)をベースに、紐づく実圃場の作付け情報を集約 +- [ ] 作付け未設定の区画も出力(空欄または「未設定」) +- [ ] ダウンロード前にプレビュー表示できる + +**【集計ロジック】** +- 水稲共済と同様、中山間マスタをループして実圃場を集約 → PDF生成 + +--- + +### P1-5: 前年度作付け計画のコピー + +**As a** 農家 +**I want** 前年度の作付け計画を丸ごと新年度にコピーしたい +**So that** 毎年ゼロから入力せずに、微調整だけで済む + +**【受け入れ基準】** +- [ ] 「前年度をコピー」ボタンを押すと、前年度の作付け情報が新年度に複製される +- [ ] 圃場マスタはコピーしない(マスタは共通) +- [ ] コピー後、作物の種類を個別に変更できる +- [ ] コピー前に確認ダイアログを表示(上書き防止) + +--- + +### P1-6: スマホでの圃場情報参照 + +**As a** 農家 +**I want** 田んぼにいるときに、スマホでその圃場の情報を見たい +**So that** 「この田んぼに植えた品種は何だっけ?」「面積はいくつだっけ?」をその場で確認できる + +**【受け入れ基準】** +- [ ] スマホで圃場一覧を見られる +- [ ] 検索・絞り込み機能で目的の圃場を素早く見つけられる +- [ ] 圃場詳細画面で以下を確認: + - 名称 + - 住所 + - 面積 + - 今年の作付け作物・品種 + - (将来)過去の作付け履歴 +- [ ] 文字サイズ: 16px以上 +- [ ] タップ領域: 44px×44px以上 + +--- + +## 🟡 Phase 2 - 栽培履歴機能 + +### P2-1: 播種日・定植日の記録 + +**As a** 農家 +**I want** 各圃場の播種日(種まき日)や定植日を記録したい +**So that** スマホで「いつ植えたか」を確認でき、次の作業(追肥など)のタイミングを判断できる + +**【受け入れ基準】** +- [ ] 圃場ごとに「播種日」「定植日」を入力できる +- [ ] カレンダーUIで日付を選択 +- [ ] スマホで過去の記録を閲覧できる + +--- + +### P2-2: 農薬・肥料散布の記録 + +**As a** 農家 +**I want** 除草剤や肥料をまいた日を記録したい +**So that** スマホで「いつ除草剤まいたか」を確認でき、次回の散布タイミングを判断できる + +**【受け入れ基準】** +- [ ] 圃場ごとに「作業日」「作業内容」「使用資材」を入力 +- [ ] 作業内容は選択式(播種、定植、除草剤散布、追肥、収穫など) +- [ ] スマホで作業履歴を時系列で閲覧 + +--- + +### P2-3: 作業予定のカレンダー表示 + +**As a** 農家 +**I want** 今後の作業予定をカレンダーで見たい +**So that** 「来週は何をする予定だっけ?」を俯瞰できる + +**【受け入れ基準】** +- [ ] 月間カレンダーで作業予定を表示 +- [ ] 各圃場の作業予定を色分け +- [ ] 日付をクリックすると、その日の作業一覧を表示 + +--- + +## 🟢 Phase 3 - 資材計画機能 + +### P3-1: 種苗必要量の自動計算 + +**As a** 農家 +**I want** 今年の作付け計画から、必要な種苗の量を自動計算してほしい +**So that** 種の発注漏れや過剰発注を防げる + +**【受け入れ基準】** +- [ ] 作物ごとに「面積あたり必要量」をマスタ登録 +- [ ] 作付け計画から、作物別の合計面積を算出 +- [ ] 必要量を一覧表示(例: にこまる 30kg、トウモロコシ 5袋) + +--- + +### P3-2: 肥料・農薬の必要量計算 + +**As a** 農家 +**I want** 施肥計画や農薬散布計画を立てたい +**So that** 資材の購入計画を立てられる + +**【受け入れ基準】** +- [ ] 作物ごとの施肥基準をマスタ登録 +- [ ] 作付け面積から必要な肥料量を計算 +- [ ] 農薬も同様に計算 + +--- + +## 🔵 Phase 4 - 収穫・販売管理 + +### P4-1: 収穫記録 + +**As a** 農家 +**I want** 収穫量を記録したい +**So that** 計画と実績を比較し、来年の計画精度を上げられる + +--- + +### P4-2: お米販売システムとの連携 + +**As a** 農家 +**I want** 作付け計画と収穫実績を、お米販売システムに自動連携したい +**So that** 在庫管理や販売計画を効率化できる + +--- + +## 📊 優先度マトリクス + +| ストーリー | 優先度 | Phase | 工数(想定) | +|-----------|--------|-------|------------| +| P1-1: 作付け計画一覧 | 🔴 高 | 1 | 2日 | +| P1-2: 作物割当 | 🔴 高 | 1 | 3日 | +| P1-3: 水稲共済PDF出力 | 🔴 高 | 1 | 3日 | +| P1-4: 中山間PDF出力 | 🔴 高 | 1 | 2日 | +| P1-5: 前年度コピー | 🔴 高 | 1 | 1日 | +| P1-6: スマホ参照 | 🔴 高 | 1 | 1日 | +| P2-1: 播種日記録 | 🟡 中 | 2 | 2日 | +| P2-2: 作業履歴 | 🟡 中 | 2 | 3日 | +| P2-3: カレンダー | 🟡 中 | 2 | 3日 | +| P3-1: 種苗計算 | 🟢 低 | 3 | 2日 | +| P3-2: 資材計算 | 🟢 低 | 3 | 2日 | +| P4-1: 収穫記録 | 🔵 将来 | 4 | TBD | +| P4-2: 販売連携 | 🔵 将来 | 4 | TBD | + +--- + +## 🎯 Phase 1 完成の定義 + +以下が全て完了したら、Phase 1(MVP)は完成とする: + +1. **機能要件** + - [ ] 作付け計画を登録・編集できる + - [ ] 水稲共済細目書のPDFを出力できる + - [ ] 中山間交付金申請のPDFを出力できる + - [ ] 前年度の作付けをコピーできる + - [ ] スマホで圃場情報を参照できる + +2. **品質要件** + - [ ] PCで快適に操作できる(レスポンス1秒以内) + - [ ] スマホで見やすい(文字サイズ16px以上) + - [ ] 出力されるPDFが正確で見やすい(手動検証でOK) + - [ ] PDFをA4用紙に印刷してそのまま提出できる + +3. **ユーザビリティ** + - [ ] 作付け計画の登録が10分以内で完了する + - [ ] 申請書のダウンロードが3クリック以内で完了する + - [ ] スマホでの圃場検索が3タップ以内で完了する diff --git a/03_データ仕様書.md b/03_データ仕様書.md new file mode 100644 index 0000000..c3b3364 --- /dev/null +++ b/03_データ仕様書.md @@ -0,0 +1,418 @@ +# データ仕様書 + +## 📊 データ構造の全体像 + +このシステムで扱うデータは3種類: + +1. **実圃場データ**(吉田農地台帳.ods)- 実際に作業する農地 +2. **共済マスタ**(水稲共済細目用.ods)- 申請書用の区画 +3. **中山間マスタ**(中山間.ods)- 申請書用の区画 + +**紐付けの関係:** +- 実圃場 → 共済区画: **M対1**(複数の実圃場が1つの共済区画に対応) +- 実圃場 → 中山間区画: **M対1**(複数の実圃場が1つの中山間区画に対応) + +```mermaid +erDiagram + 実圃場 }o--|| 共済区画 : "紐づく(M:1)" + 実圃場 }o--|| 中山間区画 : "紐づく(M:1)" + 実圃場 ||--o{ 作付け計画 : "持つ(1:N)" + + 実圃場 { + int id PK + string 名称 + string 住所 + float 面積_反 + string 地主 + int 細目_耕地番号 "共済紐付けキー" + int 細目_分筆番号 "共済紐付けキー" + int 中山間_ID "中山間紐付けキー" + } + + 共済区画 { + int id PK + string 地名_地番 + int 耕地番号 + int 分筆番号 + float 本地面積_m2 + string 漢字地名 + } + + 中山間区画 { + int id PK + int ID + string 大字 + string 字 + int 地番 + int 農地面積_m2 + int 交付金額 + } + + 作付け計画 { + int id PK + int 実圃場_id FK + int 年度 + string 作物 + string 品種 + date 播種日 + date 収穫日 + } +``` + +--- + +## 1. 実圃場データ(吉田農地台帳.ods) + +### ファイル情報 +- **行数:** 39行(39筆の圃場) +- **列数:** 7列 + +### カラム定義 + +| カラム名 | データ型 | 必須 | 説明 | 例 | +|---------|---------|-----|------|---| +| 名称 | string | ○ | 圃場の名称(自由記述) | "口神 1反2畝" | +| 住所 | string | ○ | 圃場の住所 | "口神ノ川198(笹ヶ谷374-1)" | +| 面積(反) | float | ○ | 面積(単位: 反)※1反=1000m2=10a | 1.20 | +| 地主 | string | ○ | 地主の氏名 | "山崎 出祥" | +| 細目_耕地番号 | int | ○ | 共済マスタとの紐付けキー(1/2) | 2 | +| 細目_分筆番号 | int | ○ | 共済マスタとの紐付けキー(2/2) | 1 | +| 中山間_ID | int | △ | 中山間マスタとの紐付けキー | 50 | + +### データサンプル +``` +名称 住所 面積(反) 細目_耕地番号 細目_分筆番号 中山間_ID +口神 1反2畝 口神ノ川198(笹ヶ谷374-1) 1.20 2 1 50 +口神 北東 口神ノ川198(笹ヶ谷374-1) 0.40 2 2 50 +口神 北中 口神ノ川198(笹ヶ谷374-1) 0.43 2 2 50 +``` + +### 特記事項 +- **中山間_IDは一部NULL**: 39筆中2筆が中山間の対象外(`NaN`) +- **同じ共済区画に複数の実圃場**: 例えば共済キー「2-2」には3つの実圃場が紐づく +- **面積単位**: DB内部では「反」と「m2」の両方を保持する(変換: 1反=1000m2) + +--- + +## 2. 共済マスタ(水稲共済細目用.ods) + +### ファイル情報 +- **行数:** 31行(31区画) +- **列数:** 5列 + +### カラム定義 + +| カラム名 | データ型 | 必須 | 説明 | 例 | +|---------|---------|-----|------|---| +| 地名 地番 | string | ○ | 地名と地番(スペース区切り) | "四万十町 ササガタニ 374-1" | +| 耕地番号 | int | ○ | 共済区画の識別子(1/2) | 2 | +| 分筆番号 | int | ○ | 共済区画の識別子(2/2) | 1 | +| 本地面積 (m2) | float | ○ | 申請上の面積(単位: m2) | 25.4 | +| 漢字地名 | string | ○ | 漢字表記の地名 | "四万十町 笹ヶ谷 374-1" | + +### データサンプル +``` +地名 地番 耕地番号 分筆番号 本地面積(m2) 漢字地名 +四万十町 ササガタニ 374-1 2 1 25.4 四万十町 笹ヶ谷 374-1 +四万十町 ササガタニ 374-1 2 2 12.0 四万十町 笹ヶ谷 374-1 +``` + +### 特記事項 +- **面積の不整合は許容**: 役場データが古いため、実圃場の合計面積と一致しないことがある + - 例: 共済キー「2-2」の面積は12.0m2だが、実圃場の合計は1.33反=1330m2 + - これは「そういうもの」として扱い、システム側で修正しない +- **重複キーなし**: (耕地番号, 分筆番号)の組み合わせは一意 + +--- + +## 3. 中山間マスタ(中山間.ods) + +### ファイル情報 +- **行数:** 71行(71区画) +- **列数:** 17列(うち使用するのは一部) + +### カラム定義(主要なもの) + +| カラム名 | データ型 | 必須 | 説明 | 例 | +|---------|---------|-----|------|---| +| ID | int | ○ | 中山間区画の識別子 | 50 | +| 大字 | string | ○ | 大字名 | "口神ノ川" | +| 字 | string | ○ | 字名 | "壱町切" | +| 地番 | int | ○ | 地番 | 1694 | +| 農地面積 | int | ○ | 面積(単位: m2) | 2900 | +| 作付け品目 | string | △ | (役場が記入、システムでは上書き) | "ニラ" | +| 交付金額 | int | △ | 交付金額 | 37700 | + +### データサンプル +``` +ID 大字 字 地番 農地面積 作付け品目 交付金額 +50 口神ノ川 笹ヶ谷 374 2698 米 xxxxxx +``` + +### 特記事項 +- **使用する列は限定的**: システムでは主に「ID」「大字」「字」「地番」「農地面積」を使用 +- **作付け品目は上書き**: 役場が記入した「作付け品目」は参考情報で、システムで上書きする +- **面積の不整合は許容**: 共済マスタと同様、実圃場との差異は受け入れる + +--- + +## 4. 作付け計画データ(システム内部) + +### テーブル定義 + +| カラム名 | データ型 | 必須 | 説明 | +|---------|---------|-----|------| +| id | int | ○ | 主キー(自動採番) | +| field_id | int | ○ | 実圃場ID(外部キー) | +| year | int | ○ | 年度(2025など) | +| crop | string | ○ | 作物(「米」「トウモロコシ」など) | +| variety | string | △ | 品種(「にこまる」など) | +| planting_date | date | △ | 播種日/定植日(Phase 2) | +| harvest_date | date | △ | 収穫日(Phase 2) | +| notes | text | △ | 備考 | + +### 制約 +- **ユニーク制約**: (field_id, year) - 1つの圃場に対して1年度につき1つの作付け計画のみ + - Phase 2で二毛作対応する場合は、この制約を見直す + +--- + +## 5. 作物マスタ + +### 作付けしない +- 休耕 +- 緑肥 +- 景観作物 +- その他野菜 + +### 作付けする + +#### 米 +- 品種: + - にこまる + - たちはるか + - たちはるか(特栽) + +#### トウモロコシ +- 品種: 自由入力(毎年変わるため) + +#### エンドウ +- 品種: 久留米豊 + +#### 野菜 +- 品種: 自由入力(増減あり) + +--- + +## 6. データインポート仕様 + +### 初期セットアップ時 + +1. **共済マスタのインポート** + - `水稲共済細目用.ods` を読み込み + - `OfficialKyosaiField` テーブルに保存 + +2. **中山間マスタのインポート** + - `中山間.ods` を読み込み + - `OfficialChusankanField` テーブルに保存 + +3. **実圃場データのインポート** + - `吉田農地台帳.ods` を読み込み + - `Field` テーブルに保存 + - 同時に共済・中山間マスタとの紐付けを確立: + - `細目_耕地番号` + `細目_分筆番号` → `OfficialKyosaiField.id` を外部キーとして保存 + - `中山間_ID` → `OfficialChusankanField.id` を外部キーとして保存 + +### 紐付けロジック + +```python +# 共済マスタとの紐付け +kyosai_record = OfficialKyosaiField.objects.get( + k_num=row['細目_耕地番号'], + s_num=row['細目_分筆番号'] +) +field.kyosai_field_ref = kyosai_record.id + +# 中山間マスタとの紐付け +if pd.notna(row['中山間_ID']): + chusankan_record = OfficialChusankanField.objects.get( + c_id=int(row['中山間_ID']) + ) + field.chusankan_field_ref = chusankan_record.id +``` + +--- + +## 7. 申請書PDF出力ロジック + +### 水稲共済細目書 + +**出力形式:** +- A4サイズ、縦向き +- ヘッダー: 「水稲共済細目書(◯◯年度)」 +- 表形式(罫線あり) +- フォントサイズ: 10pt +- ページ番号(複数ページの場合) + +**表の列:** +``` +耕地番号 | 分筆番号 | 地名地番 | 漢字地名 | 本地面積(m2) | 作付品目 | 品種 | 備考 + 1 | 1 | 四万十町... | 四万十町... | 2.2 | 米 |にこまる| + 2 | 1 | 四万十町... | 四万十町... | 25.4 | 米 |にこまる| + 2 | 2 | 四万十町... | 四万十町... | 12.0 |米,野菜|にこまる,トマト|複数圃場 +``` + +**集計ロジック:** +```python +def generate_kyosai_pdf(year): + # 1. データ集約(CSVと同じロジック) + output_rows = [] + for kyosai in OfficialKyosaiField.objects.all().order_by('k_num', 's_num'): + fields = Field.objects.filter(kyosai_field_ref=kyosai.id) + plans = Plan.objects.filter(field__in=fields, year=year) + + crops = list(set([p.crop.name for p in plans if p.crop])) + varieties = list(set([p.variety.name for p in plans if p.variety])) + + row = { + '耕地番号': kyosai.k_num, + '分筆番号': kyosai.s_num, + '地名地番': kyosai.address, + '漢字地名': kyosai.kanji_name, + '本地面積(m2)': kyosai.area, + '作付品目': ','.join(crops) if crops else '未設定', + '品種': ','.join(varieties) if varieties else '', + '備考': f'{len(fields)}筆合算' if len(fields) > 1 else '' + } + output_rows.append(row) + + # 2. HTMLテンプレートで表を生成 + html = render_to_string('reports/kyosai_template.html', { + 'year': year, + 'rows': output_rows + }) + + # 3. HTML → PDF変換 + pdf = HTML(string=html).write_pdf() + return pdf +``` + +**HTMLテンプレート例(reports/kyosai_template.html):** +```html + + + + + + + +

水稲共済細目書({{ year }}年度)

+ + + + + + + + + + + + + + + {% for row in rows %} + + + + + + + + + + + {% endfor %} + +
耕地番号分筆番号地名地番漢字地名本地面積(m2)作付品目品種備考
{{ row.耕地番号 }}{{ row.分筆番号 }}{{ row.地名地番 }}{{ row.漢字地名 }}{{ row.本地面積(m2) }}{{ row.作付品目 }}{{ row.品種 }}{{ row.備考 }}
+ + +``` + +### 中山間交付金申請 + +**出力形式:** +- A4サイズ、縦向き +- ヘッダー: 「中山間地域等直接支払交付金(◯◯年度)」 +- 表形式(罫線あり) + +**表の列:** +``` +ID | 大字 | 字 | 地番 | 農地面積(m2) | 作付品目 | 品種 | 備考 +50 | 口神ノ川 | 笹ヶ谷 | 374 | 2698 | 米,野菜 | にこまる,トマト | 7筆合算 +``` + +**集計ロジック:** +- 水稲共済と同様、中山間マスタをループして実圃場を集約 → HTMLテンプレート → PDF + +--- + +## 8. データ移行・メンテナンス + +### 年度更新 +- **圃場マスタ**: 年度をまたいで共通(更新不要) +- **作付け計画**: 年度ごとに独立(前年度コピー機能で複製) + +### マスタデータの更新 +- **共済・中山間マスタ**: 役場から新しいファイルをもらった場合、再インポート + - 既存データは上書きせず、差分を確認してマージ + - または、全削除→再インポートの2段階処理 + +### バックアップ +- 全テーブルをCSV/Excelでエクスポート可能にする +- 最低5年分のデータを保持(補助金監査対応) + +--- + +## 9. 面積単位の扱い + +システム内部では以下のように統一: + +| 表示単位 | DB保存 | 変換式 | +|---------|--------|--------| +| 反(たん) | m2 | 1反 = 1000m2 | +| アール(a) | m2 | 1a = 100m2 | +| ヘクタール(ha) | m2 | 1ha = 10000m2 | + +**実装方針:** +- DB内部は全て `m2` で保存(整数型) +- 表示時にユーザー設定に応じて変換(デフォルトは「反」) +- 入力時は「反」で受け付け、内部で `m2` に変換 + +--- + +## 10. データ整合性チェック + +### チェック項目 + +1. **紐付けの存在確認** + - 実圃場の `細目_耕地番号`/`細目_分筆番号` が共済マスタに存在するか + - 実圃場の `中山間_ID` が中山間マスタに存在するか + +2. **面積の整合性(参考情報)** + - 1つの共済区画に紐づく実圃場の合計面積と、共済マスタの面積を比較 + - ⚠️ 不整合があっても警告のみ(修正はしない) + +3. **作付け未設定の検出** + - 指定年度で作付け計画が未設定の圃場をリストアップ + +### 実装 +- インポート時にバリデーションを実行 +- 管理画面で「データ整合性レポート」を表示 diff --git a/04_画面設計書.md b/04_画面設計書.md new file mode 100644 index 0000000..f3a7871 --- /dev/null +++ b/04_画面設計書.md @@ -0,0 +1,575 @@ +# 画面設計書 + +## 🎨 デザイン原則(再掲) + +1. **シンプル・イズ・ベスト**: 1画面1機能 +2. **情報の優先順位**: 最重要情報を最も目立つ位置に +3. **エラーを起こしにくい**: 選択式優先、自由入力最小限 +4. **スマホファースト(参照時)**: 文字16px以上、タップ領域44px以上 +5. **既存データを尊重**: 紙の台帳と同じ感覚で使える + +--- + +## 📱 画面一覧 + +### Phase 1(MVP) +1. **ログイン画面** +2. **ダッシュボード**(将来拡張用、Phase 1では簡易版) +3. **作付け計画一覧**(メイン画面) +4. **作付け計画編集**(モーダル/サイドパネル) +5. **圃場詳細**(スマホ参照用) +6. **申請書ダウンロード** +7. **データ管理**(インポート・エクスポート) + +### Phase 2以降 +8. 栽培履歴入力 +9. 作業カレンダー +10. 資材計画 + +--- + +## 画面1: ログイン画面 + +### 目的 +シンプルな認証(メール+パスワード) + +### レイアウト(PC/スマホ共通) + +``` +┌─────────────────────────────────────┐ +│ │ +│ 🌾 Keina System │ +│ 作付け計画管理システム │ +│ │ +│ ┌───────────────────────┐ │ +│ │ メールアドレス │ │ +│ │ [___________________] │ │ +│ └───────────────────────┘ │ +│ │ +│ ┌───────────────────────┐ │ +│ │ パスワード │ │ +│ │ [___________________] │ │ +│ └───────────────────────┘ │ +│ │ +│ [ ログイン ] │ +│ │ +└─────────────────────────────────────┘ +``` + +### 機能要件 +- [ ] メールアドレスとパスワードで認証 +- [ ] ログイン成功 → ダッシュボードへ遷移 +- [ ] ログイン失敗 → エラーメッセージ表示 +- [ ] 「パスワードを忘れた」リンク(Phase 2) + +--- + +## 画面2: ダッシュボード + +### 目的 +システムの入り口(Phase 1では簡易版、将来拡張) + +### レイアウト(PC) + +``` +┌────────────────────────────────────────────────────┐ +│ 🌾 Keina System 2025年度 ▼ 👤ログアウト │ +├────────────────────────────────────────────────────┤ +│ │ +│ 📊 概要 │ +│ ┌────────────────┬────────────────┬─────────────┐│ +│ │ 全圃場数 │ 作付け済み │ 未割当 ││ +│ │ 39筆 │ 35筆 │ 4筆 ❗ ││ +│ └────────────────┴────────────────┴─────────────┘│ +│ │ +│ 🔗 クイックアクセス │ +│ ┌──────────────────────────────────────────────┐│ +│ │ [📝 作付け計画を編集] [📄 申請書ダウンロード] ││ +│ └──────────────────────────────────────────────┘│ +│ │ +│ 📌 最近の変更 │ +│ • 2025/02/10: 田A に「米(にこまる)」を割当 │ +│ • 2025/02/09: 田B を「休耕」に変更 │ +│ │ +└────────────────────────────────────────────────────┘ +``` + +### 機能要件(Phase 1) +- [ ] 年度選択(ドロップダウン) +- [ ] 作付け状況のサマリー表示 +- [ ] 「作付け計画を編集」→ 画面3へ +- [ ] 「申請書ダウンロード」→ 画面6へ + +--- + +## 画面3: 作付け計画一覧(メイン画面) + +### 目的 +全圃場の作付け状況を一覧で確認・編集 + +### レイアウト(PC) + +``` +┌──────────────────────────────────────────────────────────────┐ +│ 🌾 Keina System > 作付け計画一覧 2025年度 ▼ 👤ログアウト │ +├──────────────────────────────────────────────────────────────┤ +│ │ +│ 🔍 [検索: 圃場名・住所_____________] 🔽絞込: [全て▼] [作物▼]│ +│ │ +│ ☐ 未割当のみ表示 [📋 前年度をコピー] [📊 申請書作成] │ +│ │ +├──────────────────────────────────────────────────────────────┤ +│ No. ☐ 名称 住所 面積 作付 品種 操作│ +├──────────────────────────────────────────────────────────────┤ +│ 1 ☐ おまけ 口神ノ川足川 351 0.2反 米 にこまる [編集]│ +│ 2 ☐ 口神1反2畝 口神ノ川198... 1.2反 米 にこまる [編集]│ +│ 3 ☐ 口神 北東 口神ノ川198... 0.4反 野菜 トマト [編集]│ +│ 4 ☐ 口神 北中 口神ノ川198... 0.4反 ❗未設定 [割当]│ +│ 5 ☐ 口神 北西 口神ノ川198... 0.5反 休耕 - [編集]│ +│ │ +│ ... (39行) │ +│ │ +├──────────────────────────────────────────────────────────────┤ +│ ページ: 1 / 2 表示件数: [25件▼] │ +└──────────────────────────────────────────────────────────────┘ +``` + +### レイアウト(スマホ) + +``` +┌────────────────────────────────┐ +│ 🌾 作付け計画 2025年度 ▼ ☰ │ +├────────────────────────────────┤ +│ 🔍 [検索_______________] [🔽] │ +├────────────────────────────────┤ +│ ┌────────────────────────────┐│ +│ │ おまけ ││ +│ │ 口神ノ川足川 351 ││ +│ │ 0.2反 | 米(にこまる) ││ +│ │ [編集] ││ +│ └────────────────────────────┘│ +│ │ +│ ┌────────────────────────────┐│ +│ │ 口神 北中 ││ +│ │ 口神ノ川198... ││ +│ │ 0.4反 | ❗未設定 ││ +│ │ [割当] ││ +│ └────────────────────────────┘│ +│ │ +│ ... (39圃場) │ +│ │ +└────────────────────────────────┘ +``` + +### 機能要件 +- [ ] 全圃場を一覧表示(39行) +- [ ] 各行に以下を表示: + - チェックボックス(一括操作用) + - 名称 + - 住所 + - 面積(反) + - 作付け作物(未設定の場合は警告色) + - 品種 + - [編集]ボタン +- [ ] 検索機能: + - 圃場名・住所で部分一致検索 + - リアルタイム絞り込み +- [ ] フィルタ機能: + - 作物で絞り込み(米、野菜、休耕など) + - 「未割当のみ」トグル +- [ ] 一括操作: + - 複数行を選択 → 「一括割当」ボタン表示 + - 同じ作物を一括で割り当て +- [ ] [前年度をコピー]ボタン: + - 確認ダイアログ表示 + - 前年度の作付けを全圃場にコピー +- [ ] [申請書作成]ボタン: + - 画面6へ遷移 + +### デザインノート +- **未割当の強調**: 赤または黄色の背景色 +- **チェックボックスの位置**: 行の左端(スマホでも押しやすい) +- **スマホ版**: カード型レイアウト(1圃場1カード) + +--- + +## 画面4: 作付け計画編集(モーダル) + +### 目的 +個別の圃場に作物を割り当てる + +### レイアウト(PC - モーダルウィンドウ) + +``` +┌────────────────────────────────────┐ +│ 作付け計画を編集 [×] │ +├────────────────────────────────────┤ +│ │ +│ 圃場: 口神 北中 │ +│ 住所: 口神ノ川198(笹ヶ谷374-1) │ +│ 面積: 0.4反 (400m2) │ +│ │ +│ ───────────────────────────────── │ +│ │ +│ 作物 * │ +│ ┌────────────────────────────────┐│ +│ │ [米 ▼] ││ +│ └────────────────────────────────┘│ +│ │ +│ 品種 │ +│ ┌────────────────────────────────┐│ +│ │ [にこまる ▼] ││ +│ └────────────────────────────────┘│ +│ │ +│ 備考 │ +│ ┌────────────────────────────────┐│ +│ │ [_____________________________]││ +│ └────────────────────────────────┘│ +│ │ +│ [キャンセル] [保存] │ +│ │ +└────────────────────────────────────┘ +``` + +### 作物選択のドロップダウン + +``` +┌────────────────────────────────────┐ +│ 作付けしない │ +│ • 休耕 │ +│ • 緑肥 │ +│ • 景観作物 │ +│ • その他野菜 │ +│ │ +│ 作付けする │ +│ • 米 │ +│ • トウモロコシ │ +│ • エンドウ │ +│ • 野菜 │ +└────────────────────────────────────┘ +``` + +### 品種選択(作物に応じて動的に変化) + +**作物=「米」の場合:** +``` +┌────────────────────────────────────┐ +│ • にこまる │ +│ • たちはるか │ +│ • たちはるか(特栽) │ +│ • [その他: 自由入力_____________] │ +└────────────────────────────────────┘ +``` + +**作物=「トウモロコシ」の場合:** +``` +┌────────────────────────────────────┐ +│ [自由入力_____________________] │ +│ (品種は毎年変わるため) │ +└────────────────────────────────────┘ +``` + +**作物=「休耕」などの場合:** +``` +(品種選択フィールドは非表示) +``` + +### 機能要件 +- [ ] 作物をドロップダウンで選択 +- [ ] 作物に応じて品種選択が動的に変化 +- [ ] 「作付けしない」系の作物は、品種選択を非表示 +- [ ] 自由入力欄を用意(トウモロコシ、野菜など) +- [ ] [保存]ボタン → 作付け計画を保存して一覧に戻る +- [ ] [キャンセル]ボタン → 変更を破棄して一覧に戻る + +### 一括割当の場合 +- モーダルのタイトルを「一括割当」に変更 +- 「圃場: 5件選択中」と表示 +- 保存時、選択中の全圃場に同じ作付けを適用 + +--- + +## 画面5: 圃場詳細(スマホ参照用) + +### 目的 +田んぼにいるときに、その圃場の情報を確認 + +### レイアウト(スマホ) + +``` +┌────────────────────────────────┐ +│ ← 一覧に戻る 2025年度 │ +├────────────────────────────────┤ +│ │ +│ 口神 北中 │ +│ │ +│ 📍 住所 │ +│ 口神ノ川198(笹ヶ谷374-1) │ +│ │ +│ 📏 面積 │ +│ 0.4反 (400m2) │ +│ │ +│ 🌾 今年の作付け │ +│ 米(にこまる) │ +│ │ +│ 📋 共済情報 │ +│ 耕地番号: 2 / 分筆: 2 │ +│ │ +│ 📋 中山間情報 │ +│ ID: 50 │ +│ │ +│ ───────────────────────────── │ +│ │ +│ 📅 過去の作付け履歴(Phase 2) │ +│ • 2024年: 米(にこまる) │ +│ • 2023年: 米(にこまる) │ +│ • 2022年: 休耕 │ +│ │ +└────────────────────────────────┘ +``` + +### 機能要件 +- [ ] 圃場の基本情報を見やすく表示 +- [ ] 文字サイズ: 18px以上(スマホで見やすく) +- [ ] 余白: 十分に確保(誤タップ防止) +- [ ] 将来的に栽培履歴も表示(Phase 2) + +--- + +## 画面6: 申請書ダウンロード + +### 目的 +水稲共済細目書・中山間交付金のPDFをダウンロード + +### レイアウト(PC) + +``` +┌────────────────────────────────────────────────────┐ +│ 🌾 Keina System > 申請書ダウンロード 👤ログアウト │ +├────────────────────────────────────────────────────┤ +│ │ +│ 年度: [2025年度 ▼] │ +│ │ +│ ┌────────────────────────────────────────────────┐│ +│ │ 📄 水稲共済細目書 ││ +│ │ ││ +│ │ 提出時期: 2月・5月(年2回) ││ +│ │ 区画数: 31区画 ││ +│ │ ││ +│ │ ⚠️ 未割当の圃場: 4筆 ││ +│ │ → 作付け計画を完成させてください ││ +│ │ ││ +│ │ [プレビュー] [PDFダウンロード] ││ +│ └────────────────────────────────────────────────┘│ +│ │ +│ ┌────────────────────────────────────────────────┐│ +│ │ 📄 中山間地域等直接支払交付金 ││ +│ │ ││ +│ │ 提出時期: 5月(年1回) ││ +│ │ 区画数: 71区画 ││ +│ │ ││ +│ │ ✅ 全て割当済み ││ +│ │ ││ +│ │ [プレビュー] [PDFダウンロード] ││ +│ └────────────────────────────────────────────────┘│ +│ │ +└────────────────────────────────────────────────────┘ +``` + +### 機能要件 +- [ ] 年度を選択 +- [ ] 各申請書について: + - 区画数を表示 + - 未割当の警告(ある場合) + - [プレビュー]ボタン → 新しいタブでPDFプレビュー + - [PDFダウンロード]ボタン → ファイルダウンロード +- [ ] ダウンロードされるPDFのファイル名: + - 水稲共済: `水稲共済細目書_2025年度.pdf` + - 中山間: `中山間交付金_2025年度.pdf` +- [ ] PDFはA4サイズ、印刷してそのまま提出可能 + +### プレビュー機能 + +[プレビュー]ボタンをクリックすると、新しいタブでPDFを表示: + +``` +┌────────────────────────────────────────────────────┐ +│ ← 戻る 水稲共済細目書_2025年度.pdf [印刷] [⬇] │ +├────────────────────────────────────────────────────┤ +│ │ +│ 水稲共済細目書(2025年度) │ +│ │ +│ ┌────────────────────────────────────────────────┐│ +│ │ 耕地 分筆 地名地番 面積 作付 品種 ││ +│ │ ──────────────────────────────────────────── ││ +│ │ 1 1 四万十町 ... 2.2 米 にこまる ││ +│ │ 2 1 四万十町 ... 25.4 米 にこまる ││ +│ │ 2 2 四万十町 ... 12.0 米,野菜 ... ││ +│ │ ││ +│ │ ... (31行) ││ +│ └────────────────────────────────────────────────┘│ +│ │ +│ 1 / 2 │ +└────────────────────────────────────────────────────┘ +``` + +- [ ] ブラウザの標準PDFビューアで表示 +- [ ] 印刷ボタンで直接印刷可能 +- [ ] ダウンロードボタンでローカル保存 + +--- + +## 画面7: データ管理 + +### 目的 +圃場マスタや申請マスタのインポート・エクスポート + +### レイアウト(PC) + +``` +┌────────────────────────────────────────────────────┐ +│ 🌾 Keina System > データ管理 👤ログアウト │ +├────────────────────────────────────────────────────┤ +│ │ +│ 📥 データインポート │ +│ │ +│ タブ: [吉田農地台帳] [共済マスタ] [中山間マスタ] │ +│ │ +│ ┌────────────────────────────────────────────────┐│ +│ │ 吉田農地台帳 (実圃場データ) ││ +│ │ ││ +│ │ 最終更新: 2025/02/01 ││ +│ │ 登録圃場数: 39筆 ││ +│ │ ││ +│ │ ファイルを選択: [ファイルを選択] 📎 ││ +│ │ ││ +│ │ ⚠️ 既存データは上書きされます ││ +│ │ ││ +│ │ [プレビュー] [インポート実行] ││ +│ └────────────────────────────────────────────────┘│ +│ │ +│ 📤 データエクスポート │ +│ │ +│ • [全圃場データ (CSV)] ※バックアップ用 │ +│ • [作付け計画 (CSV)] ※バックアップ用 │ +│ • [バックアップ (全データ ZIP)] │ +│ │ +│ 📄 申請書PDF生成 │ +│ → 「申請書ダウンロード」画面へ │ +│ │ +└────────────────────────────────────────────────────┘ +``` + +### 機能要件 +- [ ] 3種類のマスタをタブで切り替え +- [ ] ファイルアップロード(ODS/Excel対応) +- [ ] プレビュー機能(インポート前に確認) +- [ ] インポート実行(確認ダイアログ付き) +- [ ] エクスポート機能(CSV/ZIP) + +--- + +## 🎨 UI共通仕様 + +### カラーパレット + +``` +プライマリカラー(緑系): + #2E7D32 濃い緑(ヘッダー、ボタン) + #4CAF50 緑(アクセント) + #81C784 淡い緑(ホバー) + +セカンダリカラー(土系): + #8D6E63 茶色(サブヘッダー) + #BCAAA4 淡い茶(背景) + +警告・状態色: + #F44336 赤(エラー、未設定) + #FF9800 オレンジ(警告) + #4CAF50 緑(成功) + #2196F3 青(情報) + +グレースケール: + #212121 ダークグレー(テキスト) + #757575 グレー(サブテキスト) + #E0E0E0 ライトグレー(ボーダー) + #FAFAFA ホワイト(背景) +``` + +### タイポグラフィ + +``` +フォント: + - システムフォント優先 + - 日本語: "Hiragino Sans", "Yu Gothic", sans-serif + - 英数字: "Roboto", "Helvetica", sans-serif + +サイズ: + - 見出し(h1): 28px / 太字 + - 見出し(h2): 22px / 太字 + - 本文: 16px / 通常 + - 小文字: 14px / 通常 + +行間: + - 本文: 1.6 + - 見出し: 1.3 +``` + +### スペーシング + +``` +余白の基本単位: 8px + + 8px: 最小余白 + 16px: 標準余白(要素間) + 24px: セクション間 + 32px: 画面の上下余白 +``` + +### レスポンシブブレークポイント + +``` +スマートフォン: 〜767px +タブレット: 768px〜1023px +PC: 1024px〜 +``` + +--- + +## 🖱️ インタラクション + +### ボタン +- **プライマリボタン**: 緑背景、白文字、影付き +- **セカンダリボタン**: 白背景、緑文字、ボーダー付き +- **ホバー**: 10%明るく、カーソルpointer +- **押下**: 5%暗く、影を小さく + +### モーダル +- **背景**: 半透明黒(opacity: 0.5) +- **アニメーション**: フェードイン(0.2秒) +- **閉じる**: 背景クリック or [×]ボタン or Escキー + +### トースト通知 +- **位置**: 画面右上 +- **表示時間**: 3秒(自動消去) +- **種類**: 成功(緑)、エラー(赤)、警告(オレンジ)、情報(青) + +--- + +## 📱 スマホ特有の配慮 + +### タップ領域 +- **最小サイズ**: 44px × 44px +- **余白**: ボタン間は最低8px + +### スクロール +- **慣性スクロール**: `-webkit-overflow-scrolling: touch` +- **無限スクロール**: 不要(全39筆なので一覧でOK) + +### 入力 +- **キーボードタイプ**: 適切に指定(email, number, textなど) +- **オートコンプリート**: 有効化 + +### ナビゲーション +- **ハンバーガーメニュー**: 右上に配置 +- **戻るボタン**: 画面左上に配置 diff --git a/05_実装優先順位.md b/05_実装優先順位.md new file mode 100644 index 0000000..d9c74a0 --- /dev/null +++ b/05_実装優先順位.md @@ -0,0 +1,315 @@ +# 実装優先順位とマイルストーン + +## 🎯 MVP(Phase 1)の完成定義 + +以下の全てが完了したら、Phase 1は完成とする: + +### 機能完成基準 +- [ ] ログイン・認証機能 +- [ ] 作付け計画の一覧表示(PC/スマホ) +- [ ] 作付け計画の編集(個別・一括) +- [ ] 水稲共済細目書のCSV出力 +- [ ] 中山間交付金のCSV出力 +- [ ] 前年度作付けのコピー機能 +- [ ] 3種類のマスタデータインポート + +### 品質基準 +- [ ] PCで快適に操作できる(レスポンス1秒以内) +- [ ] スマホで見やすい(文字16px以上) +- [ ] 出力されるCSVが正確(手動検証でOK) + +### ユーザビリティ基準 +- [ ] 作付け計画の登録が10分以内で完了 +- [ ] 申請書のダウンロードが3クリック以内 +- [ ] スマホでの圃場検索が3タップ以内 + +**目標完成日: 2025年2月末(水稲共済の1回目申請に間に合わせる)** + +--- + +## 📅 実装スケジュール(10日間想定) + +### Day 1-2: 環境構築 & 基盤実装 + +**Day 1: プロジェクトセットアップ** +- [ ] Dockerコンテナの構築 + - PostgreSQL (PostGIS拡張) + - Django (バックエンド) + - Next.js (フロントエンド) +- [ ] Djangoプロジェクト初期化 + - `django-admin startproject` + - GIS設定 (`GDAL`, `GEOS`) + - 環境変数管理 (`.env`) +- [ ] Next.jsプロジェクト初期化 + - `create-next-app` + - Tailwind CSS設定 + - 環境変数管理 + +**Day 2: 認証機能** +- [ ] Django: ユーザーモデル (メール認証) +- [ ] Django: JWT認証設定 (`djoser`, `djangorestframework-simplejwt`) +- [ ] Next.js: ログイン画面 +- [ ] Next.js: 認証コンテキスト (`AuthContext`) +- [ ] 動作確認: ログイン→ダッシュボード遷移 + +--- + +### Day 3-4: データモデル & インポート機能 + +**Day 3: データベース設計** +- [ ] Django: モデル定義 + - `Field` (実圃場) + - `OfficialKyosaiField` (共済マスタ) + - `OfficialChusankanField` (中山間マスタ) + - `Plan` (作付け計画) + - `Crop` (作物マスタ) + - `Variety` (品種マスタ) +- [ ] マイグレーション実行 +- [ ] 管理画面での動作確認 + +**Day 4: インポート機能** +- [ ] Django: インポートAPI実装 + - `pandas` + `odfpy` でODS読み込み + - 共済マスタインポート + - 中山間マスタインポート + - 吉田農地台帳インポート(紐付け処理含む) +- [ ] Next.js: データ管理画面 + - ファイルアップロードUI + - プレビュー表示 + - インポート実行ボタン +- [ ] 動作確認: 実際のODSファイルをインポート + +--- + +### Day 5-6: 作付け計画機能(コア機能) + +**Day 5: 作付け計画API** +- [ ] Django: 作物・品種マスタの初期データ投入 +- [ ] Django: 作付け計画API + - 一覧取得 (`GET /api/plans/?year=2025`) + - 作成・更新 (`POST /api/plans/`, `PATCH /api/plans/{id}/`) + - 一括更新 (`POST /api/plans/bulk/`) + - 前年度コピー (`POST /api/plans/copy_from_previous_year/`) +- [ ] API動作確認 (Postman or curl) + +**Day 6: 作付け計画UI** +- [ ] Next.js: 作付け計画一覧画面 + - テーブル表示 + - 検索・フィルタ機能 + - 未割当のハイライト +- [ ] Next.js: 作付け計画編集モーダル + - 作物選択(ドロップダウン) + - 品種選択(動的に変化) + - 一括割当対応 +- [ ] Next.js: 前年度コピーボタン +- [ ] 動作確認: 作付け計画を実際に入力 + +--- + +### Day 7-8: 申請書出力機能 + +**Day 7: 申請書ロジック実装** +- [ ] Django: 水稲共済細目書PDF生成 + - 共済マスタをベースに集約 + - 紐づく実圃場の作付けを名寄せ + - HTMLテンプレート作成 + - WeasyPrintでPDF変換 +- [ ] Django: 中山間交付金PDF生成 + - 中山間マスタをベースに集約 + - HTMLテンプレート作成 +- [ ] 動作確認: PDFの内容と見た目を手動チェック + +**Day 8: 申請書ダウンロードUI** +- [ ] Next.js: 申請書ダウンロード画面 + - 年度選択 + - プレビュー表示(新しいタブでPDF) + - PDFダウンロードボタン +- [ ] 動作確認: PDFをダウンロードして印刷してみる + +--- + +### Day 9: スマホ対応 & UI調整 + +**Day 9: レスポンシブ対応** +- [ ] Next.js: スマホ用レイアウト調整 + - 作付け計画一覧(カード型) + - 圃場詳細画面 + - タップ領域の調整 +- [ ] 文字サイズ・余白の調整 +- [ ] 動作確認: 実機またはChrome DevToolsのモバイルビュー + +--- + +### Day 10: テスト & 微調整 + +**Day 10: 総合テスト** +- [ ] 全機能の動作確認 + - ログイン→作付け計画編集→申請書ダウンロードの一連の流れ + - 前年度コピー→編集→保存 + - スマホでの参照 +- [ ] バグ修正 +- [ ] ドキュメント整備(README.md、使い方ガイド) +- [ ] 本番デプロイ準備(Dockerイメージのビルド) + +--- + +## 🔧 技術スタックの詳細 + +### バックエンド (Django) + +| 項目 | 採用技術 | 理由 | +|------|---------|------| +| フレームワーク | Django 5.0 | 安定性、豊富なエコシステム | +| REST API | Django REST Framework | 標準的なAPI構築ツール | +| 認証 | djoser + SimpleJWT | JWT認証の簡単な実装 | +| GIS | GeoDjango (PostGIS) | 地理情報の扱いに最適 | +| ファイル解析 | pandas + odfpy | ODS/Excelの読み込みに対応 | +| PDF生成 | WeasyPrint | HTML→PDF変換、日本語対応 | +| データベース | PostgreSQL 16 + PostGIS 3.4 | 空間データの保存・検索 | + +### フロントエンド (Next.js) + +| 項目 | 採用技術 | 理由 | +|------|---------|------| +| フレームワーク | Next.js 14 (App Router) | SSR/SSG対応、モダンな開発体験 | +| スタイリング | Tailwind CSS | 高速なUI開発 | +| 状態管理 | React Context API | シンプルな認証状態管理 | +| HTTPクライアント | fetch API (native) | 軽量、標準API | +| テーブル | react-table (TanStack Table) | 高機能なテーブルコンポーネント | + +### インフラ (Docker) + +| 項目 | 採用技術 | 理由 | +|------|---------|------| +| コンテナ化 | Docker Compose | 開発・本番環境の統一 | +| データベース | postgis/postgis:16-3.4 | PostGIS公式イメージ | +| リバースプロキシ | Nginx (開発環境) | 静的ファイル配信 | + +--- + +## 📦 実装の粒度(コードレベル) + +### 最小限の実装で済むもの +- ログイン画面: メール+パスワードのみ(パスワードリセットは後回し) +- ダッシュボード: サマリー表示のみ(グラフは不要) +- 地図機能: Phase 1では不要(住所テキストのみ) + +### しっかり作り込むもの +- 作付け計画一覧: 検索・フィルタ・ソート機能 +- 編集モーダル: 入力バリデーション、エラー表示 +- 申請書CSV: 正確なデータ集計ロジック + +### Phase 2以降に回すもの +- 栽培履歴(播種日、作業記録) +- カレンダー表示 +- 資材計画 +- 過去年度の比較 + +--- + +## 🚀 デプロイ計画 + +### 開発環境 +- ローカルマシン: Docker Compose +- URL: `http://localhost:3000` + +### 本番環境(Phase 1後) +- サーバー: VPS or クラウド(AWS/GCP/さくらVPS) +- ドメイン: `keina.example.com` (仮) +- HTTPS: Let's Encrypt +- リバースプロキシ: Traefik or Nginx + +### バックアップ戦略 +- データベース: 毎日自動バックアップ(`pg_dump`) +- ファイル: CSVエクスポートでユーザー自身がバックアップ + +--- + +## 🧪 テスト戦略 + +### Phase 1では自動テスト不要 +- **理由**: シングルユーザー、手動検証で十分 +- **代わりに**: 手動チェックリストで品質保証 + +### 手動チェックリスト + +#### 機能テスト +- [ ] ログインできる +- [ ] 作付け計画を登録できる +- [ ] 作付け計画を編集できる +- [ ] 一括割当ができる +- [ ] 前年度コピーができる +- [ ] 水稲共済PDFをダウンロードできる +- [ ] 中山間PDFをダウンロードできる +- [ ] PDFをプレビュー表示できる +- [ ] スマホで圃場詳細を見られる + +#### データ整合性テスト +- [ ] 共済PDFの耕地番号が正しい +- [ ] 中山間PDFのIDが正しい +- [ ] 作物の名寄せが正しい(重複排除) +- [ ] 未割当の圃場が適切に扱われる +- [ ] PDFの表レイアウトが整っている +- [ ] PDFをA4用紙に印刷して見やすい + +#### UI/UXテスト +- [ ] PCで見やすい(文字サイズ、余白) +- [ ] スマホで見やすい(タップ領域、スクロール) +- [ ] エラーメッセージがわかりやすい +- [ ] ローディング中の表示 + +--- + +## 🔍 潜在的な技術的課題と対策 + +### 課題1: PostGISの設定 +**問題**: Dockerコンテナ内でGDAL/GEOSのパスが通らない +**対策**: 公式PostGISイメージを使用、Djangoの`GDAL_LIBRARY_PATH`を明示的に設定 + +### 課題2: ODSファイルの文字コード +**問題**: 日本語の文字化け +**対策**: `pd.read_excel(..., engine='odf')` でUTF-8として読み込み + +### 課題3: 作物の名寄せロジック +**問題**: 複数の実圃場が1つの共済区画に紐づく場合の集約 +**対策**: Pythonのセットで重複排除 → カンマ区切りで結合 + +### 課題4: スマホでのテーブル表示 +**問題**: 横スクロールが発生 +**対策**: カード型レイアウトに変更(Tailwindの`@media`クエリ) + +### 課題5: 大量データのパフォーマンス +**問題**: Phase 2以降、栽培履歴が増えると遅くなる +**対策**: ページネーション、インデックス最適化(Phase 2で対応) + +--- + +## 📝 開発時の注意点 + +### コーディング規約 +- **Python**: PEP 8準拠、型ヒント推奨 +- **JavaScript**: ESLint + Prettier、関数コンポーネント優先 +- **命名**: 英語(camelCase or snake_case)、略語は避ける + +### コミットメッセージ +``` +feat: 作付け計画一覧APIを実装 +fix: 共済CSVの面積計算バグを修正 +docs: READMEにセットアップ手順を追加 +style: Tailwindクラスを整理 +``` + +### ブランチ戦略 +- `main`: 本番環境 +- `develop`: 開発環境 +- `feature/*`: 機能開発 + +--- + +## 🎉 Phase 1完成後の振り返り + +完成したら、以下を実施: +1. **使用感チェック**: 実際に作付け計画を入力してみる +2. **申請書検証**: 出力されたCSVを役場の書式と照合 +3. **改善点の洗い出し**: 「ここがもっとこうだったら...」を記録 +4. **Phase 2の要件整理**: 栽培履歴機能の詳細を詰める