diff --git a/server/apps/ability/migrations/0027_auto_20220519_0811.py b/server/apps/ability/migrations/0027_auto_20220519_0811.py new file mode 100644 index 0000000..2c85af1 --- /dev/null +++ b/server/apps/ability/migrations/0027_auto_20220519_0811.py @@ -0,0 +1,19 @@ +# Generated by Django 3.0.5 on 2022-05-19 00:11 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('ability', '0026_qactionitem_qaction'), + ] + + operations = [ + migrations.AlterField( + model_name='qaction', + name='qtask', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='ability.QTask'), + ), + ] diff --git a/server/apps/ability/migrations/0028_auto_20220519_0832.py b/server/apps/ability/migrations/0028_auto_20220519_0832.py new file mode 100644 index 0000000..514530a --- /dev/null +++ b/server/apps/ability/migrations/0028_auto_20220519_0832.py @@ -0,0 +1,28 @@ +# Generated by Django 3.0.5 on 2022-05-19 00:32 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ability', '0027_auto_20220519_0811'), + ] + + operations = [ + migrations.AlterField( + model_name='quali', + name='grade', + field=models.PositiveSmallIntegerField(blank=True, null=True, verbose_name='等级'), + ), + migrations.AlterField( + model_name='quali', + name='level', + field=models.CharField(blank=True, max_length=10, null=True, verbose_name='等级'), + ), + migrations.AlterField( + model_name='quali', + name='name', + field=models.CharField(blank=True, max_length=20, null=True, verbose_name='资质名称'), + ), + ] diff --git a/server/apps/ability/migrations/0029_auto_20220519_0833.py b/server/apps/ability/migrations/0029_auto_20220519_0833.py new file mode 100644 index 0000000..f5c671a --- /dev/null +++ b/server/apps/ability/migrations/0029_auto_20220519_0833.py @@ -0,0 +1,19 @@ +# Generated by Django 3.0.5 on 2022-05-19 00:33 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('system', '0020_auto_20220513_0926'), + ('ability', '0028_auto_20220519_0832'), + ] + + operations = [ + migrations.AlterField( + model_name='quali', + name='citys', + field=models.ManyToManyField(blank=True, null=True, related_name='quali_citys', to='system.City', verbose_name='备案城市'), + ), + ] diff --git a/server/apps/ability/migrations/0030_auto_20220519_0834.py b/server/apps/ability/migrations/0030_auto_20220519_0834.py new file mode 100644 index 0000000..4ce05ef --- /dev/null +++ b/server/apps/ability/migrations/0030_auto_20220519_0834.py @@ -0,0 +1,19 @@ +# Generated by Django 3.0.5 on 2022-05-19 00:34 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('system', '0020_auto_20220513_0926'), + ('ability', '0029_auto_20220519_0833'), + ] + + operations = [ + migrations.AlterField( + model_name='quali', + name='citys', + field=models.ManyToManyField(blank=True, related_name='quali_citys', to='system.City', verbose_name='备案城市'), + ), + ] diff --git a/server/apps/ability/migrations/0031_auto_20220519_0903.py b/server/apps/ability/migrations/0031_auto_20220519_0903.py new file mode 100644 index 0000000..1827238 --- /dev/null +++ b/server/apps/ability/migrations/0031_auto_20220519_0903.py @@ -0,0 +1,28 @@ +# Generated by Django 3.0.5 on 2022-05-19 01:03 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ability', '0030_auto_20220519_0834'), + ] + + operations = [ + migrations.AlterField( + model_name='quali', + name='grade', + field=models.PositiveSmallIntegerField(blank=True, null=True, verbose_name='等级1'), + ), + migrations.AlterField( + model_name='quali', + name='level', + field=models.CharField(blank=True, max_length=10, null=True, verbose_name='等级2'), + ), + migrations.AlterField( + model_name='quali', + name='type', + field=models.CharField(choices=[('CMA', 'CMA'), ('CNAS', 'CNAS'), ('OTHER', 'OTHER')], max_length=10, verbose_name='资质类型'), + ), + ] diff --git a/server/apps/ability/migrations/0032_auto_20220519_0924.py b/server/apps/ability/migrations/0032_auto_20220519_0924.py new file mode 100644 index 0000000..968757a --- /dev/null +++ b/server/apps/ability/migrations/0032_auto_20220519_0924.py @@ -0,0 +1,19 @@ +# Generated by Django 3.0.5 on 2022-05-19 01:24 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ability', '0031_auto_20220519_0903'), + ] + + operations = [ + migrations.AlterField( + model_name='qactionitem', + name='action', + field=models.CharField(choices=[('update', '更新'), ('citys:add', '新增备案城市'), ('citys:remove', '移除备案城市')], default='update', max_length=20, verbose_name='操作类型'), + preserve_default=False, + ), + ] diff --git a/server/apps/ability/models.py b/server/apps/ability/models.py index 5f0216b..fce4ce5 100644 --- a/server/apps/ability/models.py +++ b/server/apps/ability/models.py @@ -146,14 +146,14 @@ class Quali(CommonAModel): (30, '市级') ) org = models.ForeignKey(Organization, on_delete=models.CASCADE, verbose_name='所属单位') - name = models.CharField('资质名称', max_length=20) - type = models.PositiveSmallIntegerField('资质类型', choices=QUALI_TYPE_CHOICES) - grade = models.PositiveSmallIntegerField('等级') + name = models.CharField('资质名称', max_length=20, null=True, blank=True) + type = models.CharField('资质类型', choices=QUALI_TYPE_CHOICES, max_length=10) + grade = models.PositiveSmallIntegerField('等级1', null=True, blank=True) scope = models.TextField('范围', null=True, blank=True) - level = models.CharField('等级', max_length=10) + level = models.CharField('等级2', max_length=10, null=True, blank=True) province = models.ForeignKey(Province, on_delete=models.SET_NULL, null=True, blank=True) city = models.ForeignKey(City, on_delete=models.SET_NULL, null=True, blank=True, related_name='quali_city') - citys = models.ManyToManyField(City, verbose_name='备案城市', related_name='quali_citys') + citys = models.ManyToManyField(City, verbose_name='备案城市', related_name='quali_citys', blank=True) description = models.TextField('描述', null=True, blank=True) qualilib = models.ForeignKey(QualiLib, verbose_name='关联资质库', null=True, blank=True, on_delete=models.SET_NULL) @@ -205,7 +205,7 @@ class QAction(CommonBModel): afield = models.ForeignKey(Dict, null=True, blank=True, verbose_name='所属领域', on_delete=models.CASCADE, related_name='quali_afield') num = models.PositiveIntegerField('新增能力数量', null=True, blank=True) - qtask = models.ForeignKey(QOrg, on_delete=models.CASCADE) + qtask = models.ForeignKey(QTask, on_delete=models.CASCADE) value1 = JSONField('值1', null=True, blank=True, default=dict) value2 = JSONField('值2', null=True, blank=True, default=dict) confirmed = models.BooleanField('是否确认',default=False) @@ -217,7 +217,7 @@ class QActionItem(BaseModel): ('citys:add', '新增备案城市'), ('citys:remove', '移除备案城市'), ) - action = models.PositiveSmallIntegerField('操作类型', null=True, blank=True, choices=QACTIONITEM_CHOICE) + action = models.CharField('操作类型', max_length=20, choices=QACTIONITEM_CHOICE) field = models.CharField('变动字段', max_length=20) value1 = JSONField('原值', null=True, blank=True, default=dict) value2 = JSONField('新值', null=True, blank=True, default=dict) diff --git a/server/apps/ability/serializers_qtask.py b/server/apps/ability/serializers_qtask.py index 3a4f3e0..f574e72 100644 --- a/server/apps/ability/serializers_qtask.py +++ b/server/apps/ability/serializers_qtask.py @@ -23,31 +23,31 @@ class QualiListSerializer(serializers.ModelSerializer): class QTaskCreateUpdateSerializer(serializers.ModelSerializer): orgs = serializers.PrimaryKeyRelatedField(label='上报公司ID列表', - queryset = Organization.objects.all(), many=True) + queryset = Organization.objects.all(), many=True, write_only=True) class Meta: model = QTask fields = ['name', 'end_date', 'orgs'] - @transaction.atomic def create(self, validated_data): - orgs = validated_data.pop('orgs') - validated_data['state'] = '待发布' - instance = super().create(validated_data) - for i in orgs: - QOrg.objects.get_or_create(qtask=instance, org=i, - defaults={'qtask': instance, 'org': i}) - return instance + with transaction.atomic(): + orgs = validated_data.pop('orgs') + validated_data['state'] = '待发布' + instance = super().create(validated_data) + for i in orgs: + QOrg.objects.get_or_create(qtask=instance, org=i, + defaults={'qtask': instance, 'org': i}) + return instance - @transaction.atomic def update(self, instance, validated_data): - if instance.state == '已关闭': - raise ParseError('任务已关闭,不可更改') - orgs = validated_data.pop('orgs') - instance = super().update(instance, validated_data) - for i in orgs: - QOrg.objects.get_or_create(qtask=instance, org=i, - defaults={'qtask': instance, 'org': i}) - return instance + with transaction.atomic(): + if instance.state == '已关闭': + raise ParseError('任务已关闭,不可更改') + orgs = validated_data.pop('orgs') + instance = super().update(instance, validated_data) + for i in orgs: + QOrg.objects.get_or_create(qtask=instance, org=i, + defaults={'qtask': instance, 'org': i}) + return instance class QTaskListSerializer(serializers.ModelSerializer): @@ -93,16 +93,18 @@ class QActionServiceSerializer(serializers.ModelSerializer): return super().create(validated_data) class QualiCreateSerializer(serializers.ModelSerializer): - citys = serializers.PrimaryKeyRelatedField(queryset=City.objects.all(), many=True, label='城市ID列表') + citys = serializers.ListField(child=serializers.IntegerField(), label='城市ID列表') class Meta: model = Quali fields = ['name', 'type', 'grade', 'scope', 'level', 'province', 'city', 'description', 'citys'] + class QualiUpdateSerializer(serializers.ModelSerializer): - citys = serializers.PrimaryKeyRelatedField(queryset=City.objects.all(), many=True, label='城市ID列表') + citys = serializers.ListField(child=serializers.IntegerField(), label='城市ID列表') + id = serializers.IntegerField(label='修改资质的ID') class Meta: model = Quali - fields = ['name', 'type', 'grade', 'scope', 'level', 'province', 'city', 'description', 'id', 'citys'] + fields = ['scope', 'level', 'description', 'id', 'citys'] class QualiSerializer(serializers.ModelSerializer): class Meta: @@ -114,9 +116,11 @@ class QActionQualiCreateSerializer(serializers.ModelSerializer): class Meta: model = QAction fields = ['qtask', 'value2'] - + def create(self, validated_data): validated_data['action'] = 'quali:create' + if validated_data['value2']['type'] in ['CMA', 'CNAS']: + validated_data['value2']['name'] = validated_data['value2']['type'] return super().create(validated_data) class QActionQualiUpdateSerializer(serializers.ModelSerializer): @@ -126,7 +130,7 @@ class QActionQualiUpdateSerializer(serializers.ModelSerializer): fields = ['qtask', 'value2'] def create(self, validated_data): - validated_data['action'] = 'quali:create' + validated_data['action'] = 'quali:update' return super().create(validated_data) class QActionACreateSerializer(serializers.ModelSerializer): diff --git a/server/apps/ability/views_qtask.py b/server/apps/ability/views_qtask.py index 9db18a0..63d7e9b 100644 --- a/server/apps/ability/views_qtask.py +++ b/server/apps/ability/views_qtask.py @@ -2,11 +2,12 @@ from rest_framework.viewsets import GenericViewSet from rest_framework.mixins import ListModelMixin, CreateModelMixin, UpdateModelMixin, DestroyModelMixin, RetrieveModelMixin from rest_framework.decorators import action from apps.ability.models import QAction, QActionItem, QOrg, QTask, Quali, QualiLib -from apps.ability.serializers_qtask import QActionDetailSerializer, QActionListSerializer, QActionQualiCreateSerializer, QActionQualiUpdateSerializer, QActionServiceSerializer, QOrgListSerializer, QTaskCreateUpdateSerializer, QTaskListSerializer, QualiCreateSerializer, QualiLibListSerializer, QualiListSerializer, QualiSerializer, QualiUpdateSerializer +from apps.ability.serializers_qtask import QActionACreateSerializer, QActionDetailSerializer, QActionListSerializer, QActionQualiCreateSerializer, QActionQualiUpdateSerializer, QActionServiceSerializer, QOrgListSerializer, QTaskCreateUpdateSerializer, QTaskListSerializer, QualiCreateSerializer, QualiLibListSerializer, QualiListSerializer, QualiSerializer, QualiUpdateSerializer from django.db import transaction from rest_framework.response import Response from rest_framework import status from apps.system.mixins import CreateUpdateCustomMixin +from apps.system.models import City from utils.pagination import PageOrNot from rest_framework.exceptions import ParseError from rest_framework import serializers @@ -26,8 +27,24 @@ class QualiViewSet(ListModelMixin, GenericViewSet): search_fields = ['name', 'type', 'grade'] ordering = ['org__sort'] + @action(methods=['get'], detail=False, perms_map = {'get':'*'}) + def my(self, request, *args, **kwargs): + """ + 我的资质 + """ + user = self.request.user + queryset = self.filter_queryset(self.get_queryset().filter(org=user.dept)) + + page = self.paginate_queryset(queryset) + if page is not None: + serializer = self.get_serializer(page, many=True) + return self.get_paginated_response(serializer.data) + + serializer = self.get_serializer(queryset, many=True) + return Response(serializer.data) + class QTaskViewSet(CreateUpdateCustomMixin, CreateModelMixin, ListModelMixin, UpdateModelMixin, GenericViewSet): - perms_map = {'get': 'qtask_view', 'post': 'qtask_create'} + perms_map = {'get': 'qtask_view', 'post': 'qtask_create', 'put': 'qtask_update'} queryset = QTask.objects.all() serializer_class = QTaskListSerializer ordering = ['-create_time'] @@ -37,7 +54,7 @@ class QTaskViewSet(CreateUpdateCustomMixin, CreateModelMixin, ListModelMixin, Up return QTaskCreateUpdateSerializer return super().get_serializer_class() - @action(methods=['put'], detail=True, perms_map = {'put':'qtask_start'}) + @action(methods=['put'], detail=True, perms_map = {'put':'qtask_start'}, serializer_class=serializers.Serializer) @transaction.atomic def start(self, request, *args, **kwargs): """ @@ -47,7 +64,6 @@ class QTaskViewSet(CreateUpdateCustomMixin, CreateModelMixin, ListModelMixin, Up if obj.state == '待发布': obj.state = '进行中' obj.save() - QOrg.objects.filter(qtask=obj).update(state='待报送') return Response() return Response('任务状态错误', status=status.HTTP_400_BAD_REQUEST) @@ -58,13 +74,13 @@ class QOrgViewSet(ListModelMixin, GenericViewSet): serializer_class = QOrgListSerializer ordering = ['-create_time'] - @action(methods=['get'], detail=False, perms_map = {'post':'qtask_my'}) + @action(methods=['get'], detail=False, perms_map = {'get':'qtask_my'}) def my(self, request, *args, **kwargs): """ 我的报送任务 """ user = self.request.user - queryset = self.filter_queryset(self.get_queryset().filter(org=user.dept)) + queryset = self.filter_queryset(self.get_queryset().filter(org=user.dept).exclude(qtask__state='待发布')) page = self.paginate_queryset(queryset) if page is not None: @@ -81,7 +97,6 @@ def cal_count(qtask, org): qorg.count_confirmed = qs.filter(confirmed=True).count() qorg.save() - class QActionViewSet(PageOrNot, ListModelMixin, DestroyModelMixin, RetrieveModelMixin,GenericViewSet): perms_map = {'get': 'qtask_view'} queryset = QAction.objects.select_related('file', 'atype', 'afield', 'qtask', 'belong_dept') @@ -95,7 +110,7 @@ class QActionViewSet(PageOrNot, ListModelMixin, DestroyModelMixin, RetrieveModel return QActionDetailSerializer return super().get_serializer_class() - @action(methods=['get'], detail=False, perms_map = {'post':'qaction_my'}) + @action(methods=['get'], detail=False, perms_map = {'get':'qaction_my'}) def my(self, request, *args, **kwargs): """ 我的报送操作 @@ -144,7 +159,6 @@ class QActionViewSet(PageOrNot, ListModelMixin, DestroyModelMixin, RetrieveModel serializer.save(create_by=user, belong_dept=user.dept) vdata = serializer.validated_data cal_count(vdata['qtask'], user.dept) - # 更新资质库 return Response() @action(methods=['post'], detail=False, @@ -157,13 +171,30 @@ class QActionViewSet(PageOrNot, ListModelMixin, DestroyModelMixin, RetrieveModel user = request.user serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) - serializer.save(create_by=user, belong_dept=user.dept) + obj = serializer.save(create_by=user, belong_dept=user.dept) vdata = serializer.validated_data + # 比较差别 + quali = Quali.objects.get(id=obj.value2.get('id')) + old_data = QualiSerializer(instance=quali).data + obj.value1 = old_data + for k, v in obj.value2.items(): + if v != old_data[k]: + QActionItem.objects.create(action='update', field=k, value1=old_data[k], value2=v, qaction=obj) + if k == 'citys': + old_citys = set(old_data['citys']) + new_citys = set(v) + removes = old_citys.difference(new_citys) + adds = new_citys.difference(old_citys) + for i in removes: + QActionItem.objects.create(action='city:remove', field='citys', city=City.objects.get(id=i), qaction=obj) + for i in adds: + QActionItem.objects.create(action='city:add', field='citys', city=City.objects.get(id=i), qaction=obj) + cal_count(vdata['qtask'], user.dept) return Response() @action(methods=['post'], detail=False, - perms_map = {'post':'ability_create'}, serializer_class=QActionQualiCreateSerializer) + perms_map = {'post':'ability_create'}, serializer_class=QActionACreateSerializer) @transaction.atomic def ability_create(self, request, *args, **kwargs): """ @@ -187,36 +218,33 @@ class QActionViewSet(PageOrNot, ListModelMixin, DestroyModelMixin, RetrieveModel obj = self.get_object() if obj.confirmed: raise ParseError('该动作已确认') - if obj.action == 'service.update': - org = obj.org - org.service = obj.service + if obj.action == 'service:update': + org = obj.belong_dept + org.service = obj.value2 org.save() elif obj.action == 'quali:create': serializer = QualiCreateSerializer(data=obj.value2) - serializer.save(org=obj.belong_dept, create_by=obj.create_by) + serializer.is_valid(raise_exception=True) + instance = serializer.save(org=obj.belong_dept, create_by=obj.create_by) + if instance.type == 'OTHER': + qualiLib, _ = QualiLib.objects.get_or_create(name=instance.name) + levels = qualiLib.levels + levels.append(instance.level) + le = list(set(levels)) + qualiLib.levels = le + qualiLib.save() + instance.qualilib = qualiLib + instance.save() elif obj.action == 'quali:update': quali = Quali.objects.get(id=obj.value2.get('id')) - old_data = QualiSerializer(instance=quali).data - obj.value1 = old_data s = QualiUpdateSerializer(instance=quali, data=obj.value2) + s.is_valid(raise_exception=True) s.save(update_by=obj.create_by) - # 比较差别 - for k, v in obj.value2.items(): - if v != old_data[k]: - QActionItem.objects.create(action='update', field=k, vaule1=old_data[k], value2=v, qaction=obj) - if k == 'citys': - old_citys = set(old_data['citys']) - new_citys = set(v) - removes = old_citys.difference(new_citys) - adds = new_citys.difference(old_citys) - for i in removes: - QAction.objects.create(action='city:remove', field='citys', city=i, qaction=obj) - for i in adds: - QAction.objects.create(action='city:add', field='citys', city=i, qaction=obj) elif obj.action == 'ability:create': pass obj.confirmed = True obj.save() + cal_count(obj.qtask, obj.belong_dept) return Response(status=status.HTTP_200_OK)