施肥散布実績機能を実装し運搬・作業記録・在庫連携を追加

This commit is contained in:
Akira
2026-03-17 19:28:52 +09:00
parent 865d53ed9a
commit 140d5e5a4d
31 changed files with 2053 additions and 248 deletions

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,11 @@
from django.contrib import admin
from .models import WorkRecord
@admin.register(WorkRecord)
class WorkRecordAdmin(admin.ModelAdmin):
list_display = ['work_date', 'work_type', 'title', 'year', 'auto_created']
list_filter = ['work_type', 'year', 'auto_created']
search_fields = ['title']

View File

@@ -0,0 +1,8 @@
from django.apps import AppConfig
class WorkrecordsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'apps.workrecords'
verbose_name = '作業記録'

View File

@@ -0,0 +1,36 @@
# Generated by Django 5.0 on 2026-03-17 08:49
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
('fertilizer', '0008_spreadingsession_fertilizationentry_actual_bags_and_more'),
]
operations = [
migrations.CreateModel(
name='WorkRecord',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('work_date', models.DateField(verbose_name='作業日')),
('work_type', models.CharField(choices=[('fertilizer_delivery', '肥料運搬'), ('fertilizer_spreading', '肥料散布')], max_length=40, verbose_name='作業種別')),
('title', models.CharField(max_length=200, verbose_name='タイトル')),
('year', models.IntegerField(verbose_name='年度')),
('auto_created', models.BooleanField(default=True, verbose_name='自動生成')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('delivery_trip', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='work_record', to='fertilizer.deliverytrip', verbose_name='運搬回')),
('spreading_session', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='work_record', to='fertilizer.spreadingsession', verbose_name='散布実績')),
],
options={
'verbose_name': '作業記録',
'verbose_name_plural': '作業記録',
'ordering': ['-work_date', '-updated_at', '-id'],
},
),
]

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,44 @@
from django.db import models
class WorkRecord(models.Model):
class WorkType(models.TextChoices):
FERTILIZER_DELIVERY = 'fertilizer_delivery', '肥料運搬'
FERTILIZER_SPREADING = 'fertilizer_spreading', '肥料散布'
work_date = models.DateField(verbose_name='作業日')
work_type = models.CharField(
max_length=40,
choices=WorkType.choices,
verbose_name='作業種別',
)
title = models.CharField(max_length=200, verbose_name='タイトル')
year = models.IntegerField(verbose_name='年度')
auto_created = models.BooleanField(default=True, verbose_name='自動生成')
delivery_trip = models.OneToOneField(
'fertilizer.DeliveryTrip',
on_delete=models.CASCADE,
null=True,
blank=True,
related_name='work_record',
verbose_name='運搬回',
)
spreading_session = models.OneToOneField(
'fertilizer.SpreadingSession',
on_delete=models.CASCADE,
null=True,
blank=True,
related_name='work_record',
verbose_name='散布実績',
)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
ordering = ['-work_date', '-updated_at', '-id']
verbose_name = '作業記録'
verbose_name_plural = '作業記録'
def __str__(self):
return f'{self.work_date} {self.get_work_type_display()}'

View File

@@ -0,0 +1,38 @@
from rest_framework import serializers
from .models import WorkRecord
class WorkRecordSerializer(serializers.ModelSerializer):
work_type_display = serializers.CharField(source='get_work_type_display', read_only=True)
delivery_plan_id = serializers.SerializerMethodField()
delivery_plan_name = serializers.SerializerMethodField()
class Meta:
model = WorkRecord
fields = [
'id',
'work_date',
'work_type',
'work_type_display',
'title',
'year',
'auto_created',
'delivery_trip',
'delivery_plan_id',
'delivery_plan_name',
'spreading_session',
'created_at',
'updated_at',
]
def get_delivery_plan_id(self, obj):
if obj.delivery_trip_id:
return obj.delivery_trip.delivery_plan_id
return None
def get_delivery_plan_name(self, obj):
if obj.delivery_trip_id:
return obj.delivery_trip.delivery_plan.name
return None

View File

@@ -0,0 +1,33 @@
from .models import WorkRecord
def sync_delivery_work_record(trip):
if trip.date is None:
WorkRecord.objects.filter(delivery_trip=trip).delete()
return
WorkRecord.objects.update_or_create(
delivery_trip=trip,
defaults={
'work_date': trip.date,
'work_type': WorkRecord.WorkType.FERTILIZER_DELIVERY,
'title': f'肥料運搬: {trip.delivery_plan.name} {trip.order + 1}回目',
'year': trip.delivery_plan.year,
'auto_created': True,
'spreading_session': None,
},
)
def sync_spreading_work_record(session):
WorkRecord.objects.update_or_create(
spreading_session=session,
defaults={
'work_date': session.date,
'work_type': WorkRecord.WorkType.FERTILIZER_SPREADING,
'title': f'肥料散布: {session.name.strip() or session.date}',
'year': session.year,
'auto_created': True,
'delivery_trip': None,
},
)

View File

@@ -0,0 +1,12 @@
from django.urls import include, path
from rest_framework.routers import DefaultRouter
from .views import WorkRecordViewSet
router = DefaultRouter()
router.register(r'', WorkRecordViewSet, basename='workrecord')
urlpatterns = [
path('', include(router.urls)),
]

View File

@@ -0,0 +1,22 @@
from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticated
from .models import WorkRecord
from .serializers import WorkRecordSerializer
class WorkRecordViewSet(viewsets.ReadOnlyModelViewSet):
serializer_class = WorkRecordSerializer
permission_classes = [IsAuthenticated]
def get_queryset(self):
queryset = WorkRecord.objects.select_related(
'delivery_trip',
'delivery_trip__delivery_plan',
'spreading_session',
)
year = self.request.query_params.get('year')
if year:
queryset = queryset.filter(year=year)
return queryset