feat: ofm-vehicle 修改车辆审批申请 改完按时间段进行选择
This commit is contained in:
parent
2799e85605
commit
e093498d3c
|
|
@ -1,5 +1,5 @@
|
||||||
from django_filters import rest_framework as filters
|
from django_filters import rest_framework as filters
|
||||||
from apps.ofm.models import MroomBooking, BorrowRecord
|
from apps.ofm.models import MroomBooking, BorrowRecord, Vehicle
|
||||||
|
|
||||||
from .models import LendingSeal
|
from .models import LendingSeal
|
||||||
from apps.utils.filters import MyJsonListFilter
|
from apps.utils.filters import MyJsonListFilter
|
||||||
|
|
@ -15,6 +15,16 @@ class MroomBookingFilterset(filters.FilterSet):
|
||||||
"id": ["exact"]
|
"id": ["exact"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class VehicleFilterset(filters.FilterSet):
|
||||||
|
class Meta:
|
||||||
|
model = Vehicle
|
||||||
|
fields = {
|
||||||
|
'slot_vehicle__vehreg': ['exact', 'in'],
|
||||||
|
'slot_vehicle__vdate': ['exact', 'gte', 'lte'],
|
||||||
|
'create_by': ['exact'],
|
||||||
|
"id": ["exact"]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class SealFilter(filters.FilterSet):
|
class SealFilter(filters.FilterSet):
|
||||||
seal = MyJsonListFilter(label='按印章名称查询', field_name="seal")
|
seal = MyJsonListFilter(label='按印章名称查询', field_name="seal")
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
# Generated by Django 3.2.12 on 2025-11-17 08:15
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import django.utils.timezone
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
('ofm', '0031_auto_20251106_1608'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='VehicleReg',
|
||||||
|
fields=[
|
||||||
|
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
|
||||||
|
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
|
||||||
|
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
|
||||||
|
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
|
||||||
|
('name', models.CharField(max_length=50, verbose_name='车辆名称')),
|
||||||
|
('brand', models.CharField(blank=True, max_length=50, null=True, verbose_name='品牌')),
|
||||||
|
('plate', models.CharField(max_length=50, verbose_name='车牌号')),
|
||||||
|
('km', models.PositiveIntegerField(blank=True, null=True, verbose_name='行驶里程')),
|
||||||
|
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='vehiclereg_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
|
||||||
|
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='vehiclereg_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='vehicle',
|
||||||
|
name='end_time',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='vehicle',
|
||||||
|
name='start_time',
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='vehicle',
|
||||||
|
name='reception',
|
||||||
|
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='接待人'),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='VehicleSlot',
|
||||||
|
fields=[
|
||||||
|
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
|
||||||
|
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
|
||||||
|
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
|
||||||
|
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
|
||||||
|
('vdate', models.DateField(db_index=True, verbose_name='使用日期')),
|
||||||
|
('slot', models.PositiveIntegerField(help_text='0-47', verbose_name='时段')),
|
||||||
|
('is_inuse', models.BooleanField(default=True, verbose_name='是否占用')),
|
||||||
|
('vehicle', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='slot_vehicle', to='ofm.vehicle')),
|
||||||
|
('vehreg', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='slot_record', to='ofm.vehiclereg')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='vehicle',
|
||||||
|
name='vehiclereg',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='vehicle_record', to='ofm.vehiclereg'),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -46,10 +46,46 @@ class MroomSlot(BaseModel):
|
||||||
is_inuse = models.BooleanField('是否占用', default=True)
|
is_inuse = models.BooleanField('是否占用', default=True)
|
||||||
|
|
||||||
|
|
||||||
# class Seal(BaseModel):
|
class VehicleReg(CommonADModel):
|
||||||
# """TN: 印章类型"""
|
"""TN: 车辆台账"""
|
||||||
# name = models.CharField('印章名称', max_length=50, unique=True)
|
name = models.CharField('车辆名称', max_length=50)
|
||||||
|
brand = models.CharField('品牌', max_length=50, null=True, blank=True)
|
||||||
|
plate = models.CharField('车牌号', max_length=50)
|
||||||
|
km = models.PositiveIntegerField('行驶里程', null=True, blank=True)
|
||||||
|
|
||||||
|
class Vehicle(CommonBDModel):
|
||||||
|
"""TN: 用车申请"""
|
||||||
|
vehiclereg = models.ForeignKey(VehicleReg, on_delete=models.CASCADE, related_name="vehicle_record", null=True, blank=True)
|
||||||
|
location = models.CharField('出发地点', null=True, blank=True, max_length=100)
|
||||||
|
via = models.CharField('途经地点', null=True, blank=True, max_length=100)
|
||||||
|
destination = models.CharField('到达地点', null=True, blank=True, max_length=100)
|
||||||
|
start_km = models.PositiveIntegerField('出发公里数')
|
||||||
|
end_km = models.PositiveIntegerField('归还公里数', null=True, blank=True)
|
||||||
|
actual_km = models.PositiveIntegerField('实际行驶公里数', editable=False)
|
||||||
|
is_city = models.BooleanField('是否市内用车', default=True)
|
||||||
|
reason = models.CharField('用车事由', max_length=100)
|
||||||
|
reception = models.CharField('接待人', max_length=50, blank=True, null=True)
|
||||||
|
ticket = models.ForeignKey('wf.ticket', verbose_name='关联工单',
|
||||||
|
on_delete=models.SET_NULL, related_name='vehicle_ticket', null=True, blank=True, db_constraint=False)
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
if self.end_km:
|
||||||
|
if self.start_km <= self.end_km:
|
||||||
|
self.actual_km = self.end_km - self.start_km
|
||||||
|
with transaction.atomic():
|
||||||
|
super().save(*args, **kwargs)
|
||||||
|
VehicleReg.objects.filter(id=self.vehiclereg.id).update(km=self.end_km)
|
||||||
|
else:
|
||||||
|
raise ParseError('归还公里数不能小于出发公里数')
|
||||||
|
else:
|
||||||
|
self.actual_km = 0
|
||||||
|
return super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
class VehicleSlot(BaseModel):
|
||||||
|
vehreg = models.ForeignKey(VehicleReg, on_delete=models.CASCADE, related_name="slot_record")
|
||||||
|
vehicle = models.ForeignKey(Vehicle, on_delete=models.CASCADE, related_name="slot_vehicle")
|
||||||
|
vdate = models.DateField('使用日期', db_index=True)
|
||||||
|
slot = models.PositiveIntegerField('时段', help_text='0-47')
|
||||||
|
is_inuse = models.BooleanField('是否占用', default=True)
|
||||||
|
|
||||||
class LendingSeal(CommonBDModel):
|
class LendingSeal(CommonBDModel):
|
||||||
"""TN: 印章外出用印信息"""
|
"""TN: 印章外出用印信息"""
|
||||||
|
|
@ -69,32 +105,6 @@ class LendingSeal(CommonBDModel):
|
||||||
on_delete=models.SET_NULL, related_name='seal_ticket', null=True, blank=True, db_constraint=False)
|
on_delete=models.SET_NULL, related_name='seal_ticket', null=True, blank=True, db_constraint=False)
|
||||||
note = models.TextField('备注', null=True, blank=True)
|
note = models.TextField('备注', null=True, blank=True)
|
||||||
|
|
||||||
|
|
||||||
class Vehicle(CommonBDModel):
|
|
||||||
"""TN: 用车申请"""
|
|
||||||
start_time = models.DateTimeField('出车时间', blank=True, null=True)
|
|
||||||
end_time = models.DateTimeField('还车时间', blank=True, null=True)
|
|
||||||
location = models.CharField('出发地点', null=True, blank=True, max_length=100)
|
|
||||||
via = models.CharField('途经地点', null=True, blank=True, max_length=100)
|
|
||||||
destination = models.CharField('到达地点', null=True, blank=True, max_length=100)
|
|
||||||
start_km = models.PositiveIntegerField('出发公里数')
|
|
||||||
end_km = models.PositiveIntegerField('归还公里数', null=True, blank=True)
|
|
||||||
actual_km = models.PositiveIntegerField('实际行驶公里数', editable=False)
|
|
||||||
is_city = models.BooleanField('是否市内用车', default=True)
|
|
||||||
reason = models.CharField('用车事由', max_length=100)
|
|
||||||
ticket = models.ForeignKey('wf.ticket', verbose_name='关联工单',
|
|
||||||
on_delete=models.SET_NULL, related_name='vehicle_ticket', null=True, blank=True, db_constraint=False)
|
|
||||||
def save(self, *args, **kwargs):
|
|
||||||
if self.end_km:
|
|
||||||
if self.start_km <= self.end_km:
|
|
||||||
self.actual_km = self.end_km - self.start_km
|
|
||||||
else:
|
|
||||||
raise ParseError('归还公里数不能小于出发公里数')
|
|
||||||
else:
|
|
||||||
self.actual_km = 0
|
|
||||||
return super().save(*args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class FileRecord(CommonBDModel):
|
class FileRecord(CommonBDModel):
|
||||||
"""TN: 档案台账"""
|
"""TN: 档案台账"""
|
||||||
name = models.CharField('资料名称', max_length=100)
|
name = models.CharField('资料名称', max_length=100)
|
||||||
|
|
@ -105,7 +115,6 @@ class FileRecord(CommonBDModel):
|
||||||
reciver = models.CharField('接收人(综合办)', max_length=50, null=True, blank=True)
|
reciver = models.CharField('接收人(综合办)', max_length=50, null=True, blank=True)
|
||||||
remark = models.TextField('备注', max_length=200, null=True, blank=True)
|
remark = models.TextField('备注', max_length=200, null=True, blank=True)
|
||||||
|
|
||||||
|
|
||||||
class BorrowRecord(CommonBDModel):
|
class BorrowRecord(CommonBDModel):
|
||||||
"""TN: 借阅、复印、查阅记录"""
|
"""TN: 借阅、复印、查阅记录"""
|
||||||
borrow_file = models.ManyToManyField(FileRecord, related_name="borrow_records")
|
borrow_file = models.ManyToManyField(FileRecord, related_name="borrow_records")
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
from .models import (Mroom, MroomBooking, MroomSlot, LendingSeal, Vehicle, FileRecord, BorrowRecord, Publicity)
|
from .models import (Mroom, MroomBooking, MroomSlot, LendingSeal, Vehicle, FileRecord, BorrowRecord, Publicity, VehicleReg, VehicleSlot)
|
||||||
from apps.utils.serializers import CustomModelSerializer
|
from apps.utils.serializers import CustomModelSerializer
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
|
|
@ -57,7 +57,6 @@ class MroomBookingSerializer(CustomModelSerializer):
|
||||||
MroomSlot.objects.create(booking=booking, slot=slot, mdate=mdate, mroom=mroom, is_inuse=True)
|
MroomSlot.objects.create(booking=booking, slot=slot, mdate=mdate, mroom=mroom, is_inuse=True)
|
||||||
return booking
|
return booking
|
||||||
|
|
||||||
|
|
||||||
class MroomSlotSerializer(CustomModelSerializer):
|
class MroomSlotSerializer(CustomModelSerializer):
|
||||||
booking_title = serializers.CharField(source='booking.title', read_only=True)
|
booking_title = serializers.CharField(source='booking.title', read_only=True)
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
@ -65,6 +64,62 @@ class MroomSlotSerializer(CustomModelSerializer):
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
|
|
||||||
|
|
||||||
|
class VehicleRecordSerializer(CustomModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = VehicleReg
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
|
||||||
|
class VehicleSerializer(CustomModelSerializer):
|
||||||
|
vehreg = serializers.PrimaryKeyRelatedField(queryset=VehicleReg.objects.all(), write_only=True, label="车辆信息")
|
||||||
|
vdate = serializers.DateField(write_only=True, label="预订日期")
|
||||||
|
slots = serializers.ListField(child=serializers.IntegerField(), write_only=True, label="时段索引")
|
||||||
|
create_by_name = serializers.CharField(source='create_by.username', read_only=True)
|
||||||
|
create_by_phone = serializers.CharField(source='create_by.phone', read_only=True)
|
||||||
|
belong_dept_name = serializers.CharField(source='belong_dept.name', read_only=True)
|
||||||
|
ticket_ = TicketSimpleSerializer(source='ticket', read_only=True)
|
||||||
|
class Meta:
|
||||||
|
model = Vehicle
|
||||||
|
fields = '__all__'
|
||||||
|
read_only_fields = EXCLUDE_FIELDS + ['actual_km']
|
||||||
|
extra_kwargs = {'belong_dept': {'required': True}}
|
||||||
|
|
||||||
|
def create(self, validated_data):
|
||||||
|
vehreg = validated_data.pop('vehreg')
|
||||||
|
slots = validated_data.pop('slots')
|
||||||
|
vdate = validated_data.pop('vdate')
|
||||||
|
vehicle = super().create(validated_data)
|
||||||
|
VehicleSlot.objects.filter(vehicle=vehicle).delete()
|
||||||
|
for slot in slots:
|
||||||
|
if slot < 0 or slot > 47:
|
||||||
|
raise ParseError("时段索引超出范围")
|
||||||
|
ms_exists = VehicleSlot.objects.filter(vehreg=vehreg, vdate=vdate, slot=slot, is_inuse=True).exists()
|
||||||
|
if ms_exists:
|
||||||
|
raise ParseError("时段已预订,请刷新重选")
|
||||||
|
VehicleSlot.objects.create(vehicle=vehicle, slot=slot, vdate=vdate, vehreg=vehreg, is_inuse=True)
|
||||||
|
return vehicle
|
||||||
|
|
||||||
|
def update(self, instance, validated_data):
|
||||||
|
vehreg = validated_data.pop('vehreg')
|
||||||
|
slots = validated_data.pop('slots')
|
||||||
|
vdate = validated_data.pop('vdate')
|
||||||
|
vehicle = super().update(instance, validated_data)
|
||||||
|
VehicleSlot.objects.filter(vehicle=vehicle).delete()
|
||||||
|
for slot in slots:
|
||||||
|
if slot < 0 or slot > 47:
|
||||||
|
raise ParseError("时段索引超出范围")
|
||||||
|
ms_exists = VehicleSlot.objects.filter(vehreg=vehreg, vdate=vdate, slot=slot, is_inuse=True).exists()
|
||||||
|
if ms_exists:
|
||||||
|
raise ParseError("时段已预订,请刷新重选")
|
||||||
|
VehicleSlot.objects.create(vehicle=vehicle, slot=slot, vdate=vdate, vehreg=vehreg, is_inuse=True)
|
||||||
|
return vehicle
|
||||||
|
|
||||||
|
class VehSlotSerializer(CustomModelSerializer):
|
||||||
|
veh_name = serializers.CharField(source='vehreg.name', read_only=True)
|
||||||
|
class Meta:
|
||||||
|
model = VehicleSlot
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
class LendingSealSerializer(CustomModelSerializer):
|
class LendingSealSerializer(CustomModelSerializer):
|
||||||
create_by_name = serializers.CharField(source='create_by.name', read_only=True)
|
create_by_name = serializers.CharField(source='create_by.name', read_only=True)
|
||||||
belong_dept_name = serializers.CharField(source='belong_dept.name', read_only=True)
|
belong_dept_name = serializers.CharField(source='belong_dept.name', read_only=True)
|
||||||
|
|
@ -74,16 +129,20 @@ class LendingSealSerializer(CustomModelSerializer):
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
read_only_fields = EXCLUDE_FIELDS
|
read_only_fields = EXCLUDE_FIELDS
|
||||||
|
|
||||||
|
# class VehicleRecordSerializer(CustomModelSerializer):
|
||||||
|
# class meta:
|
||||||
|
# model = VehicleReg
|
||||||
|
# fields = '__all__'
|
||||||
|
|
||||||
class VehicleSerializer(CustomModelSerializer):
|
# class VehicleSerializer(CustomModelSerializer):
|
||||||
create_by_name = serializers.CharField(source='create_by.name', read_only=True)
|
# create_by_name = serializers.CharField(source='create_by.name', read_only=True)
|
||||||
belong_dept_name = serializers.CharField(source='belong_dept.name', read_only=True)
|
# belong_dept_name = serializers.CharField(source='belong_dept.name', read_only=True)
|
||||||
ticket_ = TicketSimpleSerializer(source='ticket', read_only=True)
|
# vehiclerd = serializers.PrimaryKeyRelatedField(queryset=VehicleReg.objects.all(), write_only=True, label="车辆记录")
|
||||||
class Meta:
|
# ticket_ = TicketSimpleSerializer(source='ticket', read_only=True)
|
||||||
model = Vehicle
|
# class Meta:
|
||||||
fields = '__all__'
|
# model = Vehicle
|
||||||
read_only_fields = EXCLUDE_FIELDS + ['actual_km']
|
# fields = '__all__'
|
||||||
|
# read_only_fields = EXCLUDE_FIELDS + ['actual_km']
|
||||||
|
|
||||||
class FileRecordSerializer(CustomModelSerializer):
|
class FileRecordSerializer(CustomModelSerializer):
|
||||||
create_by_name = serializers.CharField(source='create_by.name', read_only=True)
|
create_by_name = serializers.CharField(source='create_by.name', read_only=True)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
from django.urls import path, include
|
from django.urls import path, include
|
||||||
from rest_framework.routers import DefaultRouter
|
from rest_framework.routers import DefaultRouter
|
||||||
from apps.ofm.views import (MroomViewSet, MroomBookingViewSet, MroomSlotViewSet,LendingSealViewSet, VehicleViewSet, FilerecordViewSet, FileborrowViewSet, PublicityViewSet)
|
from apps.ofm.views import (MroomViewSet, MroomBookingViewSet, MroomSlotViewSet,LendingSealViewSet, VehicleViewSet,
|
||||||
|
VehicleRegViewSet, VehicleSlotViewSet, FilerecordViewSet, FileborrowViewSet, PublicityViewSet)
|
||||||
|
|
||||||
API_BASE_URL = 'api/ofm/'
|
API_BASE_URL = 'api/ofm/'
|
||||||
HTML_BASE_URL = 'dhtml/ofm/'
|
HTML_BASE_URL = 'dhtml/ofm/'
|
||||||
|
|
@ -12,6 +13,8 @@ router.register('mroomslot', MroomSlotViewSet, basename='mroomslot')
|
||||||
# router.register('sealmanage', SealManageViewSet, basename='sealmanage')
|
# router.register('sealmanage', SealManageViewSet, basename='sealmanage')
|
||||||
router.register('lendingseal', LendingSealViewSet, basename='lendingseal')
|
router.register('lendingseal', LendingSealViewSet, basename='lendingseal')
|
||||||
router.register('vehicle', VehicleViewSet, basename='vehicle')
|
router.register('vehicle', VehicleViewSet, basename='vehicle')
|
||||||
|
router.register('vsolt', VehicleSlotViewSet, basename='vslot')
|
||||||
|
router.register('vreg', VehicleRegViewSet, basename='vreg')
|
||||||
router.register('filerecord', FilerecordViewSet, basename='filerecord')
|
router.register('filerecord', FilerecordViewSet, basename='filerecord')
|
||||||
router.register('fileborrow', FileborrowViewSet, basename='fileborrow')
|
router.register('fileborrow', FileborrowViewSet, basename='fileborrow')
|
||||||
router.register('publicity', PublicityViewSet, basename='publicity')
|
router.register('publicity', PublicityViewSet, basename='publicity')
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from apps.utils.viewsets import CustomModelViewSet, CustomGenericViewSet
|
from apps.utils.viewsets import CustomModelViewSet, CustomGenericViewSet
|
||||||
from .models import Mroom, MroomBooking, MroomSlot, LendingSeal, Vehicle, FileRecord, BorrowRecord, Publicity
|
from .models import Mroom, MroomBooking, MroomSlot, LendingSeal, Vehicle, VehicleSlot, VehicleReg, FileRecord, BorrowRecord, Publicity
|
||||||
|
|
||||||
from .serializers import (MroomSerializer, MroomBookingSerializer, MroomSlotSerializer, LendingSealSerializer,
|
from .serializers import (MroomSerializer, MroomBookingSerializer, MroomSlotSerializer, LendingSealSerializer,
|
||||||
VehicleSerializer, FileRecordSerializer, BorrowRecordSerializer, PublicitySerializer)
|
VehicleSerializer, VehicleRecordSerializer, VehSlotSerializer, FileRecordSerializer, BorrowRecordSerializer, PublicitySerializer)
|
||||||
from apps.utils.mixins import CustomListModelMixin
|
from apps.utils.mixins import CustomListModelMixin
|
||||||
from rest_framework.exceptions import ParseError
|
from rest_framework.exceptions import ParseError
|
||||||
from apps.ofm.filters import MroomBookingFilterset, SealFilter, BorrowRecordFilter
|
from apps.ofm.filters import MroomBookingFilterset, SealFilter, BorrowRecordFilter, VehicleFilterset
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
|
||||||
class MroomViewSet(CustomModelViewSet):
|
class MroomViewSet(CustomModelViewSet):
|
||||||
|
|
@ -124,14 +124,105 @@ class LendingSealViewSet(CustomModelViewSet):
|
||||||
data_filter = True
|
data_filter = True
|
||||||
|
|
||||||
|
|
||||||
class VehicleViewSet(CustomModelViewSet):
|
class VehicleRegViewSet(CustomModelViewSet):
|
||||||
"""list: 车辆
|
"""list: 车辆
|
||||||
|
|
||||||
车辆
|
车辆
|
||||||
"""
|
"""
|
||||||
|
queryset = VehicleReg.objects.all()
|
||||||
|
serializer_class = VehicleRecordSerializer
|
||||||
|
ordering = ["-create_time"]
|
||||||
|
|
||||||
|
class VehicleViewSet(CustomModelViewSet):
|
||||||
|
"""list: 会议室预订
|
||||||
|
|
||||||
|
会议室预订
|
||||||
|
"""
|
||||||
queryset = Vehicle.objects.all()
|
queryset = Vehicle.objects.all()
|
||||||
serializer_class = VehicleSerializer
|
serializer_class = VehicleSerializer
|
||||||
ordering = ["-create_time"]
|
select_related_fields = ["create_by", "ticket", "belong_dept"]
|
||||||
|
filterset_class = VehicleFilterset
|
||||||
|
|
||||||
|
def add_info_for_list(self, data):
|
||||||
|
booking_ids = [d["id"] for d in data]
|
||||||
|
slots = VehicleSlot.objects.filter(booking__in=booking_ids).order_by("vehreg", "vehicle", "vdate", "slot")
|
||||||
|
booking_info = {}
|
||||||
|
for slot in slots:
|
||||||
|
booking_id = slot.vehicle.id
|
||||||
|
|
||||||
|
if booking_id not in booking_info:
|
||||||
|
booking_info[booking_id] = {
|
||||||
|
"vdate": slot.vdate.strftime("%Y-%m-%d"), # 格式化日期
|
||||||
|
"vehreg": slot.vehreg.id,
|
||||||
|
"vehreg_name": slot.vehreg.name, # 会议室名称
|
||||||
|
"time_ranges": [], # 存储时间段(如 ["8:00-9:00", "10:00-11:30"])
|
||||||
|
"current_slots": [], # 临时存储连续的slot(用于合并)
|
||||||
|
}
|
||||||
|
|
||||||
|
# 检查是否连续(当前slot是否紧接上一个slot)
|
||||||
|
current_slots = booking_info[booking_id]["current_slots"]
|
||||||
|
if not current_slots or slot.slot == current_slots[-1] + 1:
|
||||||
|
current_slots.append(slot.slot)
|
||||||
|
else:
|
||||||
|
# 如果不连续,先把当前连续的slot转换成时间段
|
||||||
|
if current_slots:
|
||||||
|
start_time = self._slot_to_time(current_slots[0])
|
||||||
|
end_time = self._slot_to_time(current_slots[-1] + 1)
|
||||||
|
booking_info[booking_id]["time_ranges"].append(f"{start_time}-{end_time}")
|
||||||
|
current_slots.clear()
|
||||||
|
current_slots.append(slot.slot)
|
||||||
|
|
||||||
|
# 处理最后剩余的连续slot
|
||||||
|
for info in booking_info.values():
|
||||||
|
if info["current_slots"]:
|
||||||
|
start_time = self._slot_to_time(info["current_slots"][0])
|
||||||
|
end_time = self._slot_to_time(info["current_slots"][-1] + 1)
|
||||||
|
info["time_ranges"].append(f"{start_time}-{end_time}")
|
||||||
|
info["slots"] = info.pop("current_slots") # 清理临时数据
|
||||||
|
|
||||||
|
for item in data:
|
||||||
|
item.update(booking_info.get(item["id"], {}))
|
||||||
|
return data
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _slot_to_time(slot):
|
||||||
|
"""将slot (0-47) 转换为 HH:MM 格式的时间字符串"""
|
||||||
|
hours = slot // 2
|
||||||
|
minutes = (slot % 2) * 30
|
||||||
|
return f"{hours:02d}:{minutes:02d}"
|
||||||
|
|
||||||
|
def perform_update(self, serializer):
|
||||||
|
ins:Vehicle = self.get_object()
|
||||||
|
ticket = ins.ticket
|
||||||
|
if ticket is None or ticket.state.type == 1:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
raise ParseError("存在审批单,不允许修改")
|
||||||
|
if ins.create_by and ins.create_by != self.request.user:
|
||||||
|
raise ParseError("只允许创建者修改")
|
||||||
|
return super().perform_update(serializer)
|
||||||
|
|
||||||
|
def perform_destroy(self, instance):
|
||||||
|
if instance.create_by and instance.create_by != self.request.user:
|
||||||
|
raise ParseError("只允许创建者删除")
|
||||||
|
ticket = instance.ticket
|
||||||
|
if ticket is None or ticket.state.type == 1:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
raise ParseError("存在审批单,不允许删除")
|
||||||
|
if ticket:
|
||||||
|
ticket.delete()
|
||||||
|
instance.delete()
|
||||||
|
|
||||||
|
|
||||||
|
class VehicleSlotViewSet(CustomListModelMixin, CustomGenericViewSet):
|
||||||
|
"""list:
|
||||||
|
|
||||||
|
会议室预订时段
|
||||||
|
"""
|
||||||
|
queryset = VehicleSlot.objects.all()
|
||||||
|
serializer_class = VehSlotSerializer
|
||||||
|
filterset_fields = ["vehreg", "vehicle", "vdate", "is_inuse"]
|
||||||
|
|
||||||
|
|
||||||
class FilerecordViewSet(CustomModelViewSet):
|
class FilerecordViewSet(CustomModelViewSet):
|
||||||
|
|
|
||||||
|
|
@ -115,6 +115,7 @@ class PaperRecord(CommonADModel):
|
||||||
|
|
||||||
class Platform(CommonADModel):
|
class Platform(CommonADModel):
|
||||||
"""TN: 平台信息表"""
|
"""TN: 平台信息表"""
|
||||||
|
name = models.CharField("名称", max_length=50, null=True, blank=True)
|
||||||
p_date = models.DateField(null=True, blank=True, verbose_name="日期")
|
p_date = models.DateField(null=True, blank=True, verbose_name="日期")
|
||||||
p_dept = models.CharField("归口部门", max_length=200, null=True, blank=True)
|
p_dept = models.CharField("归口部门", max_length=200, null=True, blank=True)
|
||||||
city_p = models.BooleanField("市级平台", default=False)
|
city_p = models.BooleanField("市级平台", default=False)
|
||||||
|
|
@ -128,7 +129,6 @@ class Platform(CommonADModel):
|
||||||
class Platstanding(CommonADModel):
|
class Platstanding(CommonADModel):
|
||||||
"""TN: 平台台账登记"""
|
"""TN: 平台台账登记"""
|
||||||
number = models.CharField("发文号", max_length=50, null=True, blank=True)
|
number = models.CharField("发文号", max_length=50, null=True, blank=True)
|
||||||
name = models.CharField("名称", max_length=50, null=True, blank=True)
|
|
||||||
p_type = models.CharField("平台类型", max_length=50, null=True, blank=True)
|
p_type = models.CharField("平台类型", max_length=50, null=True, blank=True)
|
||||||
org = models.CharField("单位", max_length=100, null=True, blank=True)
|
org = models.CharField("单位", max_length=100, null=True, blank=True)
|
||||||
period = models.CharField("建设期", max_length=200, null=True, blank=True)
|
period = models.CharField("建设期", max_length=200, null=True, blank=True)
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@ class PlatformSerializer(CustomModelSerializer):
|
||||||
|
|
||||||
class PlatstandingSerializer(CustomModelSerializer):
|
class PlatstandingSerializer(CustomModelSerializer):
|
||||||
platinfo_ = PlatformSerializer(source='platinfo', read_only=True)
|
platinfo_ = PlatformSerializer(source='platinfo', read_only=True)
|
||||||
|
plat_name = serializers.CharField(source='platinfo.name', read_only=True, label="平台名称")
|
||||||
create_by_name = serializers.CharField(source='create_by.name', read_only=True)
|
create_by_name = serializers.CharField(source='create_by.name', read_only=True)
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Platstanding
|
model = Platstanding
|
||||||
|
|
|
||||||
|
|
@ -103,12 +103,22 @@ class PlatformViewSet(CustomModelViewSet):
|
||||||
|
|
||||||
|
|
||||||
class PlatstandingViewSet(CustomModelViewSet):
|
class PlatstandingViewSet(CustomModelViewSet):
|
||||||
"""list: 平台审批
|
"""list: 平台台账
|
||||||
|
|
||||||
平台审批
|
平台审批
|
||||||
"""
|
"""
|
||||||
queryset = Platstanding.objects.all()
|
queryset = Platstanding.objects.all()
|
||||||
serializer_class = PlatstandingSerializer
|
serializer_class = PlatstandingSerializer
|
||||||
filterset_fields = ["name", "city_p", "province_p"]
|
filterset_fields = ["platinfo", "city_p", "province_p"]
|
||||||
ordering = ["-create_time", "name"]
|
ordering = ["-create_time"]
|
||||||
search_fields = ["name", "city_p", "province_p"]
|
search_fields = ["platinfo", "city_p", "province_p"]
|
||||||
|
|
||||||
|
@action(detail=False, methods=['get'])
|
||||||
|
def plat_name(self, request):
|
||||||
|
"""获取台账列表"""
|
||||||
|
search = request.query_params.get('search', '')
|
||||||
|
queryset = Platform.objects.all()
|
||||||
|
if search:
|
||||||
|
queryset = queryset.filter(name__icontains=search)
|
||||||
|
papers = [{'id': p.id, 'name': p.name} for p in queryset]
|
||||||
|
return Response(papers)
|
||||||
Loading…
Reference in New Issue