diff --git a/client/quota.dat b/client/quota.dat new file mode 100644 index 0000000..0b48bc8 Binary files /dev/null and b/client/quota.dat differ diff --git a/client/src/App.vue b/client/src/App.vue index 0942ea4..63ad2ed 100644 --- a/client/src/App.vue +++ b/client/src/App.vue @@ -6,6 +6,7 @@ + \ No newline at end of file diff --git a/client/src/views/plan/paichai.vue b/client/src/views/plan/paichai.vue new file mode 100644 index 0000000..f8cb32d --- /dev/null +++ b/client/src/views/plan/paichai.vue @@ -0,0 +1,220 @@ + + diff --git a/client/src/views/plan/plan.vue b/client/src/views/plan/plan.vue new file mode 100644 index 0000000..0fb054d --- /dev/null +++ b/client/src/views/plan/plan.vue @@ -0,0 +1,413 @@ + + diff --git a/client/src/views/project/project copy.vue b/client/src/views/project/project copy.vue new file mode 100644 index 0000000..0d7877c --- /dev/null +++ b/client/src/views/project/project copy.vue @@ -0,0 +1,178 @@ + + diff --git a/client/src/views/project/project.vue b/client/src/views/project/project.vue new file mode 100644 index 0000000..22df5f8 --- /dev/null +++ b/client/src/views/project/project.vue @@ -0,0 +1,316 @@ + + diff --git a/client/src/views/standard/standard.vue b/client/src/views/standard/standard.vue index 306c0b0..826922c 100644 --- a/client/src/views/standard/standard.vue +++ b/client/src/views/standard/standard.vue @@ -253,6 +253,19 @@ export default { this.$refs['Form'].clearValidate() }) }, + handleDelete(scope) { + this.$confirm('确定删除?', '警告', { + // confirmButtonText: '确定', + // cancelButtonText: '取消', + type: 'warning' + }).then(() => { + deleteStandard(scope.row.id).then(res=>{ + this.$message.success('成功') + this.getList() + }) + }) + + }, handlePreview(file) { if ('url' in file){ window.open(file.url) diff --git a/client/src/views/system/dict.vue b/client/src/views/system/dict.vue index dd15dae..e001527 100644 --- a/client/src/views/system/dict.vue +++ b/client/src/views/system/dict.vue @@ -89,14 +89,14 @@ icon="el-icon-edit" @click="handleEdit(scope)" /> - + /> @@ -297,7 +297,7 @@ export default { .then(async() => { const { code } = await deleteDict(scope.row.id) if (code>=200){ - this.dictList.splice(scope.row.index, 1) + this.getList() this.$message({ type: 'success', message: '成功删除!' diff --git a/server/apps/certset/migrations/0021_remove_evaluations_conform.py b/server/apps/certset/migrations/0021_remove_evaluations_conform.py new file mode 100644 index 0000000..00ad4c9 --- /dev/null +++ b/server/apps/certset/migrations/0021_remove_evaluations_conform.py @@ -0,0 +1,17 @@ +# Generated by Django 3.0.5 on 2020-09-03 08:58 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('certset', '0020_evaluations_conform'), + ] + + operations = [ + migrations.RemoveField( + model_name='evaluations', + name='conform', + ), + ] diff --git a/server/apps/certset/models.py b/server/apps/certset/models.py index 2741113..e228e70 100644 --- a/server/apps/certset/models.py +++ b/server/apps/certset/models.py @@ -22,7 +22,7 @@ class Standard(CommonAModel): verbose_name_plural = verbose_name def __str__(self): - return self.name + return self.code + '-' + self.name class ImplementRule(CommonAModel): @@ -44,7 +44,7 @@ class ImplementRule(CommonAModel): verbose_name_plural = verbose_name def __str__(self): - return self.name + return self.code +'-'+ self.name class UnitType(CommonAModel): @@ -73,7 +73,6 @@ class EvaluationItem(CommonAModel): return self.cert_field class Evaluations(CommonAModel): content = models.TextField('内容', blank=True) - conform = models.TextField('是否符合', default='符合') evItem = models.ForeignKey(EvaluationItem, on_delete=models.SET_NULL, null=True, blank=True, verbose_name='评审标准', related_name='Evaluations_evItem') class Meta: verbose_name = '评审列' diff --git a/server/apps/certset/serializers.py b/server/apps/certset/serializers.py index febd08e..f6532e2 100644 --- a/server/apps/certset/serializers.py +++ b/server/apps/certset/serializers.py @@ -6,31 +6,50 @@ from apps.system.serializers import DictSerializer class StandardSerializer(serializers.ModelSerializer): + fullname = serializers.SerializerMethodField(read_only=True) class Meta: model = Standard fields = '__all__' + def get_fullname(self, obj): + fullname = obj.code + ' ' + obj.name + if obj.code in obj.name: + fullname = obj.name + return fullname + class ImplementRuleSerializer(serializers.ModelSerializer): cert_field_ = DictSerializer(source='cert_field', read_only=True) + fullname = serializers.SerializerMethodField(read_only=True) class Meta: model = ImplementRule fields = '__all__' + + def get_fullname(self, obj): + fullname = obj.code + ' ' + obj.name + if obj.code in obj.name: + fullname = obj.name + return fullname class ImplementRuleListSerializer(serializers.ModelSerializer): pv_scope = DictSerializer() pv_class = DictSerializer() cert_field = DictSerializer() ccc_list = DictSerializer(many=True) + fullname = serializers.SerializerMethodField(read_only=True) class Meta: model = ImplementRule - fields = ['id', 'code', 'name', 'cert_field', 'pv_scope', 'pv_class', 'create_time', 'ccc_list'] + fields = ['id', 'code', 'name', 'cert_field', 'pv_scope', 'pv_class', 'create_time', 'ccc_list', 'fullname'] @staticmethod def setup_eager_loading(queryset): """ Perform necessary eager loading of data. """ queryset = queryset.select_related('pv_scope','pv_class', 'cert_field') queryset = queryset.prefetch_related('ccc_list',) return queryset - + def get_fullname(self, obj): + fullname = obj.code + ' ' + obj.name + if obj.code in obj.name: + fullname = obj.name + return fullname # def get_cert_field(self, obj): # return obj.get_cert_field_display() diff --git a/server/apps/certset/views.py b/server/apps/certset/views.py index 93548a2..097e194 100644 --- a/server/apps/certset/views.py +++ b/server/apps/certset/views.py @@ -21,7 +21,7 @@ class ImplementRuleViewSet(CreateUpdateCustomMixin, OptimizationMixin, ModelView queryset = ImplementRule.objects.all() serializer_class = ImplementRuleSerializer search_fields = ['name', 'code', 'cert_field'] - filterset_fields = ['pv_scope', 'cert_field', 'cert_field__code'] + filterset_fields = ['pv_scope', 'cert_field', 'cert_field__code', 'ccc_list'] ordering = ['-create_time'] def get_serializer_class(self): diff --git a/server/apps/employee/serializers.py b/server/apps/employee/serializers.py index b292e23..6d45210 100644 --- a/server/apps/employee/serializers.py +++ b/server/apps/employee/serializers.py @@ -1,3 +1,4 @@ +from utils import serializer from rest_framework import serializers from .models import * @@ -5,6 +6,8 @@ from .models import * from apps.system.serializers import DictSerializer, UserListSerializer from apps.certset.serializers import ImplementRuleSerializer + + class EmployeeSerializer(serializers.ModelSerializer): """ 普通序列化 @@ -24,6 +27,41 @@ class EmployeeSerializer(serializers.ModelSerializer): return queryset + +class TeamMemberSerializer(serializers.ModelSerializer): + """ + 选人序列化 + """ + user_ = UserListSerializer(source='user', read_only=True) + majors = serializers.SerializerMethodField() + + class Meta: + model = Employee + fields = ['code', 'user', 'user_', 'photo', 'fields', 'majors'] + + def get_majors(self, obj): + majors = obj.ability_employee.all() + m = [] + for i in majors: + n = {'major':'', 'abilitys':''} + if not i.is_paused: + if i.major: + n['major'] = i.major.code + n['abilitys'] = i.auditor_abilitys.values_list('name', flat=True) + m.append(n) + elif i.major_rule: + n['major'] = i.major_rule.code + n['abilitys'] = i.auditor_abilitys.values_list('name', flat=True) + m.append(n) + return m + + @staticmethod + def setup_eager_loading(queryset): + """ Perform necessary eager loading of data. """ + queryset = queryset.select_related('user') + return queryset + + class QualificationSerializer(serializers.ModelSerializer): """ 普通序列化 diff --git a/server/apps/employee/urls.py b/server/apps/employee/urls.py index b34b8ae..4d0e30e 100644 --- a/server/apps/employee/urls.py +++ b/server/apps/employee/urls.py @@ -9,5 +9,7 @@ router.register('ability', AbilityViewSet, basename="ability") router.register('education', EducationViewSet, basename="education") router.register('train', TrainViewSet, basename="train") urlpatterns = [ + path('employee/team/', TeamMemberView.as_view()), path('', include(router.urls)) + ] \ No newline at end of file diff --git a/server/apps/employee/views.py b/server/apps/employee/views.py index 77ce3a5..2696ef0 100644 --- a/server/apps/employee/views.py +++ b/server/apps/employee/views.py @@ -1,4 +1,5 @@ from django.shortcuts import render +from rest_framework.generics import ListAPIView from rest_framework.viewsets import ModelViewSet from rest_framework.response import Response from rest_framework import status @@ -7,6 +8,17 @@ from .serializers import * from apps.system.permission_data import RbacFilterSet from apps.system.mixins import CreateUpdateCustomMixin, OptimizationMixin # Create your views here. + + +class TeamMemberView(ListAPIView): + perms_map = {'get': '*'} + queryset = Employee.objects.all() + serializer_class = TeamMemberSerializer + filterset_fields = ['is_onjob', 'is_fulltime', 'user__dept'] + search_fields = ['code', 'remember_code', 'user__name', 'fields', 'ability_employee__major__code', 'ability_employee__major_rule__code'] + ordering = ['-pk'] + + class EmployeeViewSet(CreateUpdateCustomMixin, OptimizationMixin, ModelViewSet): """ 详细信息-增删改查 @@ -17,7 +29,7 @@ class EmployeeViewSet(CreateUpdateCustomMixin, OptimizationMixin, ModelViewSet): serializer_class = EmployeeSerializer search_fields = ['code', 'remember_code', 'user__name', 'fields'] filterset_fields = ['is_onjob', 'is_fulltime', 'user__dept'] - ordering = ['-create_time'] + ordering = ['-pk'] class QualificationViewSet(CreateUpdateCustomMixin, OptimizationMixin, ModelViewSet): diff --git a/server/apps/financial/migrations/0002_auto_20200903_1658.py b/server/apps/financial/migrations/0002_auto_20200903_1658.py new file mode 100644 index 0000000..50ac5ab --- /dev/null +++ b/server/apps/financial/migrations/0002_auto_20200903_1658.py @@ -0,0 +1,49 @@ +# Generated by Django 3.0.5 on 2020-09-03 08:58 + +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), + ('system', '0031_delete_bscodeset'), + ('financial', '0001_initial'), + ] + + operations = [ + migrations.AlterModelOptions( + name='bill', + options={'verbose_name': '收费信息', 'verbose_name_plural': '收费信息'}, + ), + migrations.RemoveField( + model_name='bill', + name='applicant', + ), + migrations.CreateModel( + name='Chargeitem', + fields=[ + ('id', models.AutoField(auto_created=True, 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='删除标记')), + ('itemkind', models.CharField(max_length=300, verbose_name='收费项目')), + ('eitemkind', models.CharField(max_length=300, verbose_name='收费英文项目')), + ('unitcount', models.FloatField(blank=True, default=0, null=True, verbose_name='单元数量')), + ('perfee', models.FloatField(blank=True, default=0, null=True, verbose_name='单价')), + ('pertotal', models.FloatField(blank=True, default=0, null=True, verbose_name='小计')), + ('remark', models.CharField(max_length=300, verbose_name='备注')), + ('belong_dept', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='chargeitem_belong_dept', to='system.Organization', verbose_name='所属部门')), + ('bills', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='chargeitem_bill', to='financial.Bill')), + ('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='chargeitem_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='chargeitem_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')), + ], + options={ + 'verbose_name': '收费项目', + 'verbose_name_plural': '收费项目', + }, + ), + ] diff --git a/server/apps/financial/migrations/0003_auto_20200907_0956.py b/server/apps/financial/migrations/0003_auto_20200907_0956.py new file mode 100644 index 0000000..734df36 --- /dev/null +++ b/server/apps/financial/migrations/0003_auto_20200907_0956.py @@ -0,0 +1,24 @@ +# Generated by Django 3.0.5 on 2020-09-07 01:56 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('financial', '0002_auto_20200903_1658'), + ] + + operations = [ + migrations.AlterField( + model_name='bill', + name='feecode', + field=models.CharField(max_length=300, unique=True, verbose_name='收费单号'), + ), + migrations.AlterField( + model_name='chargeitem', + name='bills', + field=models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='chargeitem_bill', to='financial.Bill', to_field='feecode'), + ), + ] diff --git a/server/apps/financial/migrations/0004_auto_20200907_1551.py b/server/apps/financial/migrations/0004_auto_20200907_1551.py new file mode 100644 index 0000000..734a6fa --- /dev/null +++ b/server/apps/financial/migrations/0004_auto_20200907_1551.py @@ -0,0 +1,34 @@ +# Generated by Django 3.0.5 on 2020-09-07 07:51 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('financial', '0003_auto_20200907_0956'), + ] + + operations = [ + migrations.AlterField( + model_name='chargeitem', + name='bills', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='chargeitem_bill', to='financial.Bill', to_field='feecode'), + ), + migrations.AlterField( + model_name='chargeitem', + name='eitemkind', + field=models.CharField(blank=True, max_length=300, null=True, verbose_name='收费英文项目'), + ), + migrations.AlterField( + model_name='chargeitem', + name='itemkind', + field=models.CharField(blank=True, max_length=300, null=True, verbose_name='收费项目'), + ), + migrations.AlterField( + model_name='chargeitem', + name='remark', + field=models.CharField(blank=True, max_length=300, null=True, verbose_name='备注'), + ), + ] diff --git a/server/apps/financial/migrations/0005_remove_chargeitem_eitemkind.py b/server/apps/financial/migrations/0005_remove_chargeitem_eitemkind.py new file mode 100644 index 0000000..6060e56 --- /dev/null +++ b/server/apps/financial/migrations/0005_remove_chargeitem_eitemkind.py @@ -0,0 +1,17 @@ +# Generated by Django 3.0.5 on 2020-09-07 07:55 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('financial', '0004_auto_20200907_1551'), + ] + + operations = [ + migrations.RemoveField( + model_name='chargeitem', + name='eitemkind', + ), + ] diff --git a/server/apps/financial/migrations/0006_auto_20200908_1143.py b/server/apps/financial/migrations/0006_auto_20200908_1143.py new file mode 100644 index 0000000..68c6b8c --- /dev/null +++ b/server/apps/financial/migrations/0006_auto_20200908_1143.py @@ -0,0 +1,19 @@ +# Generated by Django 3.0.5 on 2020-09-08 03:43 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('financial', '0005_remove_chargeitem_eitemkind'), + ] + + operations = [ + migrations.AlterField( + model_name='chargeitem', + name='bills', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='chargeitem_bill', to='financial.Bill'), + ), + ] diff --git a/server/apps/financial/migrations/0007_auto_20200908_1146.py b/server/apps/financial/migrations/0007_auto_20200908_1146.py new file mode 100644 index 0000000..b6d36a8 --- /dev/null +++ b/server/apps/financial/migrations/0007_auto_20200908_1146.py @@ -0,0 +1,19 @@ +# Generated by Django 3.0.5 on 2020-09-08 03:46 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('financial', '0006_auto_20200908_1143'), + ] + + operations = [ + migrations.AlterField( + model_name='chargeitem', + name='bills', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='chargeitem_bill', to='financial.Bill', to_field='feecode'), + ), + ] diff --git a/server/apps/financial/models.py b/server/apps/financial/models.py index 373738d..7a34e9b 100644 --- a/server/apps/financial/models.py +++ b/server/apps/financial/models.py @@ -10,10 +10,9 @@ class Bill(CommonBModel): """ 收费单表 """ - feecode = models.CharField('收费单号', max_length=300) + feecode = models.CharField('收费单号',unique=True, max_length=300) number = models.ForeignKey(CertApp, related_name='bill_number', on_delete=models.DO_NOTHING) feecharacter = models.ForeignKey(Dict, verbose_name='费用性质', related_name='bill_feecharacter', on_delete=models.DO_NOTHING) - applicant = models.ForeignKey(Enterprise, related_name='bill_applicant', on_delete=models.DO_NOTHING) totalfee = models.FloatField('总金额',null=True, blank=True, default=0) deductions = models.FloatField('扣减额',null=True, blank=True, default=0) exchangetype = models.CharField('外汇类型', max_length=300) @@ -21,3 +20,24 @@ class Bill(CommonBModel): paidrmb = models.FloatField('实收人名币',null=True, blank=True, default=0) paidwb = models.FloatField('实收外币',null=True, blank=True, default=0) remark = models.CharField('备注', max_length=300) + class Meta: + verbose_name = '收费信息' + verbose_name_plural = verbose_name + def __str__(self): + return self.feecode + +class Chargeitem(CommonBModel): + """ + 收费项目 + """ + bills = models.ForeignKey(Bill, related_name='chargeitem_bill',to_field='feecode',null=True, blank=True, on_delete=models.DO_NOTHING) + itemkind = models.CharField('收费项目',null=True, blank=True, max_length=300) + unitcount = models.FloatField('单元数量',null=True, blank=True, default=0) + perfee = models.FloatField('单价',null=True, blank=True, default=0) + pertotal = models.FloatField('小计',null=True, blank=True, default=0) + remark = models.CharField('备注',null=True, blank=True, max_length=300) + class Meta: + verbose_name = '收费项目' + verbose_name_plural = verbose_name + def __str__(self): + return self.itemkind \ No newline at end of file diff --git a/server/apps/financial/serializers.py b/server/apps/financial/serializers.py index 5f3111b..d3c5180 100644 --- a/server/apps/financial/serializers.py +++ b/server/apps/financial/serializers.py @@ -1,11 +1,24 @@ from rest_framework import serializers -from .models import Bill +from .models import Bill,Chargeitem class BillSerializer(serializers.ModelSerializer): class Meta: model = Bill fields = '__all__' - +class ChargeitemSerializer(serializers.ModelSerializer): + class Meta: + model = Chargeitem + fields = '__all__' +class BillsSerializer(serializers.ModelSerializer): + number_=ChargeitemSerializer(source='chargeitem_number') + class Meta: + model = Bill + fields = '__all__' + def setup_eager_loading(queryset): + """ Perform necessary eager loading of data. """ + # queryset = queryset.select_related('type','economy_class') + queryset = queryset.prefetch_related('chargeitem_bill',) + return queryset \ No newline at end of file diff --git a/server/apps/financial/urls.py b/server/apps/financial/urls.py index 8c83943..a9dc02b 100644 --- a/server/apps/financial/urls.py +++ b/server/apps/financial/urls.py @@ -1,9 +1,10 @@ from django.urls import path, include from rest_framework import routers -from .views import BillViewSet +from .views import BillViewSet,ChargeitemViewSet router = routers.DefaultRouter() router.register('bill', BillViewSet, basename="bill") +router.register('chargeitem', ChargeitemViewSet, basename="chargeitem") urlpatterns = [ path('', include(router.urls)) diff --git a/server/apps/financial/views.py b/server/apps/financial/views.py index bdab514..6a641f0 100644 --- a/server/apps/financial/views.py +++ b/server/apps/financial/views.py @@ -1,9 +1,9 @@ from django.shortcuts import render from rest_framework.viewsets import ModelViewSet -from .models import Bill +from .models import Bill,Chargeitem from utils.queryset import get_child_queryset2 -from .serializers import BillSerializer +from .serializers import BillSerializer,ChargeitemSerializer from apps.system.permission_data import RbacFilterSet from apps.system.mixins import CreateUpdateCustomMixin, OptimizationMixin # Create your views here. @@ -13,6 +13,25 @@ class BillViewSet(CreateUpdateCustomMixin, OptimizationMixin, ModelViewSet): 'put': 'Bill_update', 'delete': 'Bill_delete'} queryset = Bill.objects serializer_class = BillSerializer - search_fields = ['name','query_code', 'code'] + search_fields = ['feecode'] ordering = ['-create_time'] - + filterset_fields = ['number'] + ordering = ['-create_time'] + def paginate_queryset(self, queryset): + + if ((not self.request.query_params.get('page', None)) and (self.request.query_params.get('number', None))) or (self.paginator is None): + return None + return self.paginator.paginate_queryset(queryset, self.request, view=self) +class ChargeitemViewSet(CreateUpdateCustomMixin, OptimizationMixin, ModelViewSet): + perms_map = {'get': '*', 'post': 'chargeitem_create', + 'put': 'chargeitem_update', 'delete': 'chargeitem_delete'} + queryset = Chargeitem.objects + serializer_class = ChargeitemSerializer + search_fields = ['itemkind'] + filterset_fields = ['bills'] + ordering = ['-create_time'] + def paginate_queryset(self, queryset): + + if ((not self.request.query_params.get('page', None)) and (self.request.query_params.get('bills', None))) or (self.paginator is None): + return None + return self.paginator.paginate_queryset(queryset, self.request, view=self) \ No newline at end of file diff --git a/server/apps/plan/__init__.py b/server/apps/plan/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/server/apps/plan/admin.py b/server/apps/plan/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/server/apps/plan/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/server/apps/plan/apps.py b/server/apps/plan/apps.py new file mode 100644 index 0000000..7cf7268 --- /dev/null +++ b/server/apps/plan/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class PlanConfig(AppConfig): + name = 'plan' diff --git a/server/apps/plan/filters.py b/server/apps/plan/filters.py new file mode 100644 index 0000000..e69de29 diff --git a/server/apps/plan/migrations/0001_initial.py b/server/apps/plan/migrations/0001_initial.py new file mode 100644 index 0000000..4c6f68f --- /dev/null +++ b/server/apps/plan/migrations/0001_initial.py @@ -0,0 +1,58 @@ +# Generated by Django 3.0.7 on 2020-08-25 08:36 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('system', '0031_delete_bscodeset'), + ('project', '0026_merge_20200825_1636'), + ] + + operations = [ + migrations.CreateModel( + name='Plan', + fields=[ + ('id', models.AutoField(auto_created=True, 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=1000, verbose_name='计划名称')), + ('month', models.DateField(blank=True, null=True, verbose_name='计划审核月份')), + ('belong_dept', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='plan_belong_dept', to='system.Organization', verbose_name='所属部门')), + ('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='plan_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='plan_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')), + ], + options={ + 'verbose_name': '审核计划', + 'verbose_name_plural': '审核计划', + }, + ), + migrations.CreateModel( + name='ContactRecord', + fields=[ + ('id', models.AutoField(auto_created=True, 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='删除标记')), + ('contact_date', models.DateField(verbose_name='联系时间')), + ('content', models.TextField(verbose_name='内容')), + ('belong_dept', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='contactrecord_belong_dept', to='system.Organization', verbose_name='所属部门')), + ('certapp', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='contactrecord_certapp', to='project.CertApp', verbose_name='关联申请')), + ('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='contactrecord_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')), + ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='contactrecord_project', to='project.Project', verbose_name='所属项目')), + ('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='contactrecord_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')), + ], + options={ + 'verbose_name': '联系记录', + 'verbose_name_plural': '联系记录', + }, + ), + ] diff --git a/server/apps/plan/migrations/0002_auto_20200901_1511.py b/server/apps/plan/migrations/0002_auto_20200901_1511.py new file mode 100644 index 0000000..ce9aea2 --- /dev/null +++ b/server/apps/plan/migrations/0002_auto_20200901_1511.py @@ -0,0 +1,43 @@ +# Generated by Django 3.0.7 on 2020-09-01 07:11 + +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 = [ + ('system', '0031_delete_bscodeset'), + ('project', '0027_auto_20200825_1636'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('plan', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='plan', + name='name', + field=models.CharField(max_length=1000, unique=True, verbose_name='计划名称'), + ), + migrations.CreateModel( + name='Team', + fields=[ + ('id', models.AutoField(auto_created=True, 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='删除标记')), + ('is_leader', models.BooleanField(default=False, verbose_name='是否是组长')), + ('belong_dept', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='team_belong_dept', to='system.Organization', verbose_name='所属部门')), + ('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='team_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')), + ('identity', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='system.Dict', verbose_name='身份')), + ('member', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='成员')), + ('project', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='project.Project', verbose_name='所属项目')), + ('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='team_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/server/apps/plan/migrations/0003_auto_20200901_1555.py b/server/apps/plan/migrations/0003_auto_20200901_1555.py new file mode 100644 index 0000000..90cdd35 --- /dev/null +++ b/server/apps/plan/migrations/0003_auto_20200901_1555.py @@ -0,0 +1,42 @@ +# Generated by Django 3.0.7 on 2020-09-01 07:55 + +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 = [ + ('system', '0031_delete_bscodeset'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('project', '0027_auto_20200825_1636'), + ('plan', '0002_auto_20200901_1511'), + ] + + operations = [ + migrations.CreateModel( + name='Member', + fields=[ + ('id', models.AutoField(auto_created=True, 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='删除标记')), + ('is_leader', models.BooleanField(default=False, verbose_name='是否是组长')), + ('belong_dept', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='member_belong_dept', to='system.Organization', verbose_name='所属部门')), + ('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='member_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')), + ('identity', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='member_identity', to='system.Dict', verbose_name='身份')), + ('project', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='member_project', to='project.Project', verbose_name='所属项目')), + ('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='member_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='member_user', to=settings.AUTH_USER_MODEL, verbose_name='成员')), + ], + options={ + 'verbose_name': '审核组成员', + 'verbose_name_plural': '审核组成员', + }, + ), + migrations.DeleteModel( + name='Team', + ), + ] diff --git a/server/apps/plan/migrations/__init__.py b/server/apps/plan/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/server/apps/plan/models.py b/server/apps/plan/models.py new file mode 100644 index 0000000..419f9e9 --- /dev/null +++ b/server/apps/plan/models.py @@ -0,0 +1,57 @@ +from utils import model +from django.contrib.postgres.fields import JSONField +from django.db import models +from rest_framework.exceptions import ParseError +from simple_history.models import HistoricalRecords + +from apps.system.models import CommonAModel, CommonBModel, Dict, User +from apps.project.models import Project, CertApp +# Create your models here. + +class Plan(CommonBModel): + """ + 计划(项目组) + """ + name = models.CharField('计划名称', max_length = 1000, unique=True) + month = models.DateField('计划审核月份', null=True, blank=True) + + class Meta: + verbose_name = '审核计划' + verbose_name_plural = verbose_name + + def __str__(self): + return self.name + +class ContactRecord(CommonBModel): + """ + 联系记录 + """ + project = models.ForeignKey(Project, on_delete=models.CASCADE, verbose_name='所属项目', related_name='contactrecord_project') + certapp = models.ForeignKey(CertApp, on_delete=models.CASCADE, verbose_name='关联申请', null=True, blank=True, related_name='contactrecord_certapp') + contact_date = models.DateField('联系时间') + content = models.TextField('内容') + + class Meta: + verbose_name = '联系记录' + verbose_name_plural = verbose_name + + def __str__(self): + return self.content + +class Member(CommonBModel): + """ + 审核组成员 + """ + user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='成员', related_name='member_user') + is_leader = models.BooleanField('是否是组长', default=False) + project = models.ForeignKey(Project, on_delete=models.SET_NULL, null=True, blank=True, verbose_name='所属项目', related_name='member_project') + identity = models.ForeignKey(Dict, on_delete=models.SET_NULL, null=True, blank=True, verbose_name='身份', related_name='member_identity') + + class Meta: + verbose_name = '审核组成员' + verbose_name_plural = verbose_name + + def __str__(self): + return self.member.name + + diff --git a/server/apps/plan/serializers.py b/server/apps/plan/serializers.py new file mode 100644 index 0000000..a5a083f --- /dev/null +++ b/server/apps/plan/serializers.py @@ -0,0 +1,24 @@ +from rest_framework import serializers + +from .models import * + +from apps.system.serializers import DictSerializer, UserListSerializer +from apps.certset.serializers import StandardSerializer + +class ContactRecordSerializer(serializers.ModelSerializer): + create_by_=UserListSerializer(source='create_by', read_only=True) + class Meta: + model = ContactRecord + fields = '__all__' + +class PlanSerializer(serializers.ModelSerializer): + class Meta: + model = Plan + fields = '__all__' + +class MemberSerializer(serializers.ModelSerializer): + user_ = UserListSerializer(source='user', read_only=True) + identity_ = DictSerializer(source='identity', read_only=True) + class Meta: + model = Member + fields = '__all__' \ No newline at end of file diff --git a/server/apps/plan/tests.py b/server/apps/plan/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/server/apps/plan/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/server/apps/plan/urls.py b/server/apps/plan/urls.py new file mode 100644 index 0000000..e98f8ab --- /dev/null +++ b/server/apps/plan/urls.py @@ -0,0 +1,11 @@ +from django.urls import path, include +from .views import * +from rest_framework import routers + +router = routers.DefaultRouter() +router.register('contactrecord', ContactRecordViewSet, basename="contactrecord") +router.register('plan', PlanViewSet, basename='plan') +router.register('member', MemberViewSet, basename='member') +urlpatterns = [ + path('', include(router.urls)) +] \ No newline at end of file diff --git a/server/apps/plan/views.py b/server/apps/plan/views.py new file mode 100644 index 0000000..fd315c2 --- /dev/null +++ b/server/apps/plan/views.py @@ -0,0 +1,55 @@ +from django.shortcuts import render +import rest_framework +from rest_framework.serializers import ModelSerializer +from rest_framework.viewsets import ModelViewSet, GenericViewSet +from rest_framework.response import Response +from rest_framework import status +from .models import * +from .serializers import * +from apps.system.models import Dict +from apps.system.permission_data import RbacFilterSet +from apps.system.mixins import CreateUpdateCustomMixin, OptimizationMixin +import random +from rest_framework.decorators import action +from .filters import * +from utils.pagination import PageOrNot +# Create your views here. + +class ContactRecordViewSet(CreateUpdateCustomMixin, PageOrNot, ModelViewSet): + """ + 联系记录 + """ + perms_map = {'get': 'contactrecord_view', 'post':'contactrecord_create', 'put':'contactrecord_update','delete': 'contactrecord_delete'} + queryset = ContactRecord.objects.all() + serializer_class = ContactRecordSerializer + ordering = ['pk'] + filterset_fields = ['project', 'certapp'] + +class PlanViewSet(RbacFilterSet, PageOrNot, ModelViewSet): + """ + 计划 + """ + perms_map = {'get': 'plan_view', 'post':'plan_create', 'put':'plan_update','delete': 'plan_delete'} + queryset = Plan.objects.all() + serializer_class = PlanSerializer + ordering = ['pk'] + # filterset_fields = ['project', 'certapp'] + +class MemberViewSet(CreateUpdateCustomMixin, PageOrNot, ModelViewSet): + """ + 审核组成员 + """ + perms_map = {'get': '*', 'post':'member_create', 'put':'member_update','delete': 'member_delete'} + queryset = Member.objects.all() + serializer_class = MemberSerializer + ordering = ['pk'] + filterset_fields = ['project'] + + def create(self, request, *args, **kwargs): + serializer = self.get_serializer(data=request.data) + serializer.is_valid(raise_exception=True) + if Member.objects.filter(user = request.data['user'], project=request.data['project'], is_deleted=False).exists(): + return Response('已存在该成员', status= status.HTTP_400_BAD_REQUEST) + self.perform_create(serializer) + headers = self.get_success_headers(serializer.data) + return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) \ No newline at end of file diff --git a/server/apps/project/filters.py b/server/apps/project/filters.py new file mode 100644 index 0000000..9a70524 --- /dev/null +++ b/server/apps/project/filters.py @@ -0,0 +1,14 @@ +from django_filters import rest_framework as filters +from .models import * + +class CertAppFilter(filters.FilterSet): + noproject = filters.BooleanFilter(field_name='project', lookup_expr='isnull') + class Meta: + model = CertApp + fields = ['status', 'noproject'] + +class ProjectFilter(filters.FilterSet): + planoff = filters.BooleanFilter(field_name='plan', lookup_expr='isnull') + class Meta: + model = Project + fields = ['plan', 'planoff'] \ No newline at end of file diff --git a/server/apps/project/migrations/0019_auto_20200813_1744.py b/server/apps/project/migrations/0019_auto_20200813_1744.py new file mode 100644 index 0000000..0b9568e --- /dev/null +++ b/server/apps/project/migrations/0019_auto_20200813_1744.py @@ -0,0 +1,32 @@ +# Generated by Django 3.0.7 on 2020-08-13 09:44 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('project', '0018_certapp_level'), + ] + + operations = [ + migrations.RemoveField( + model_name='unit', + name='factory', + ), + migrations.RemoveField( + model_name='unit', + name='factory_v', + ), + migrations.RemoveField( + model_name='unit', + name='subapplication', + ), + migrations.AddField( + model_name='unit', + name='certapp', + field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='project.CertApp', verbose_name='所属业务'), + preserve_default=False, + ), + ] diff --git a/server/apps/project/migrations/0020_unit_standard.py b/server/apps/project/migrations/0020_unit_standard.py new file mode 100644 index 0000000..3ae6454 --- /dev/null +++ b/server/apps/project/migrations/0020_unit_standard.py @@ -0,0 +1,20 @@ +# Generated by Django 3.0.7 on 2020-08-17 01:48 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('certset', '0016_auto_20200805_1435'), + ('project', '0019_auto_20200813_1744'), + ] + + operations = [ + migrations.AddField( + model_name='unit', + name='standard', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='certset.Standard', verbose_name='采用标准'), + ), + ] diff --git a/server/apps/project/migrations/0021_auto_20200818_1153.py b/server/apps/project/migrations/0021_auto_20200818_1153.py new file mode 100644 index 0000000..c6ab8a4 --- /dev/null +++ b/server/apps/project/migrations/0021_auto_20200818_1153.py @@ -0,0 +1,33 @@ +# Generated by Django 3.0.7 on 2020-08-18 03:53 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('certset', '0016_auto_20200805_1435'), + ('project', '0020_unit_standard'), + ] + + operations = [ + migrations.AddField( + model_name='unit', + name='implementrule', + field=models.ForeignKey(default=6, on_delete=django.db.models.deletion.DO_NOTHING, to='certset.ImplementRule', verbose_name='采用规则'), + preserve_default=False, + ), + migrations.AlterField( + model_name='unit', + name='standard', + field=models.ForeignKey(default=2, on_delete=django.db.models.deletion.DO_NOTHING, to='certset.Standard', verbose_name='采用标准'), + preserve_default=False, + ), + migrations.AlterField( + model_name='unit', + name='unittype', + field=models.ForeignKey(default=4, on_delete=django.db.models.deletion.DO_NOTHING, to='certset.UnitType', verbose_name='单元类型'), + preserve_default=False, + ), + ] diff --git a/server/apps/project/migrations/0022_auto_20200818_1704.py b/server/apps/project/migrations/0022_auto_20200818_1704.py new file mode 100644 index 0000000..6a1550e --- /dev/null +++ b/server/apps/project/migrations/0022_auto_20200818_1704.py @@ -0,0 +1,47 @@ +# Generated by Django 3.0.7 on 2020-08-18 09:04 + +import django.contrib.postgres.fields.jsonb +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('crm', '0011_enterprise_parent'), + ('project', '0021_auto_20200818_1153'), + ] + + operations = [ + migrations.AddField( + model_name='certapp', + name='scope', + field=models.TextField(blank=True, null=True, verbose_name='认证范围'), + ), + migrations.AddField( + model_name='project', + name='assign_date', + field=models.DateField(blank=True, null=True, verbose_name='下达日期'), + ), + migrations.AddField( + model_name='project', + name='auditee', + field=models.ForeignKey(default=43, on_delete=django.db.models.deletion.DO_NOTHING, related_name='project_auditee', to='crm.Enterprise', verbose_name='受审核方'), + preserve_default=False, + ), + migrations.AddField( + model_name='project', + name='auditee_v', + field=django.contrib.postgres.fields.jsonb.JSONField(default=dict, verbose_name='受审核方'), + ), + migrations.AddField( + model_name='project', + name='number', + field=models.CharField(blank=True, max_length=100, null=True, verbose_name='项目编号'), + ), + migrations.AddField( + model_name='project', + name='remark', + field=models.TextField(blank=True, null=True, verbose_name='备注'), + ), + ] diff --git a/server/apps/project/migrations/0023_auto_20200821_0957.py b/server/apps/project/migrations/0023_auto_20200821_0957.py new file mode 100644 index 0000000..123e1f5 --- /dev/null +++ b/server/apps/project/migrations/0023_auto_20200821_0957.py @@ -0,0 +1,22 @@ +# Generated by Django 3.0.7 on 2020-08-21 01:57 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('project', '0022_auto_20200818_1704'), + ] + + operations = [ + migrations.AlterModelOptions( + name='project', + options={'verbose_name': '认证项目', 'verbose_name_plural': '认证项目'}, + ), + migrations.AddField( + model_name='project', + name='status', + field=models.CharField(choices=[('待下达', '待下达'), ('待派差', '待派差'), ('进行中', '进行中'), ('已中止', '已中止'), ('已完成', '已完成')], default='待下达', max_length=50, verbose_name='项目状态'), + ), + ] diff --git a/server/apps/project/migrations/0024_project_assign_by.py b/server/apps/project/migrations/0024_project_assign_by.py new file mode 100644 index 0000000..e9a5745 --- /dev/null +++ b/server/apps/project/migrations/0024_project_assign_by.py @@ -0,0 +1,21 @@ +# Generated by Django 3.0.7 on 2020-08-21 02:04 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('project', '0023_auto_20200821_0957'), + ] + + operations = [ + migrations.AddField( + model_name='project', + name='assign_by', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='下达人'), + ), + ] diff --git a/server/apps/project/migrations/0025_auto_20200821_1541.py b/server/apps/project/migrations/0025_auto_20200821_1541.py new file mode 100644 index 0000000..678c380 --- /dev/null +++ b/server/apps/project/migrations/0025_auto_20200821_1541.py @@ -0,0 +1,46 @@ +# Generated by Django 3.0.7 on 2020-08-21 07:41 + +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), + ('system', '0028_auto_20200807_1018'), + ('project', '0024_project_assign_by'), + ] + + operations = [ + migrations.AddField( + model_name='project', + name='can_paichai', + field=models.BooleanField(default=False, verbose_name='是否可派差'), + ), + migrations.CreateModel( + name='Plan', + fields=[ + ('id', models.AutoField(auto_created=True, 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=1000, verbose_name='计划名称')), + ('month', models.DateField(blank=True, null=True, verbose_name='计划审核月份')), + ('belong_dept', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='plan_belong_dept', to='system.Organization', verbose_name='所属部门')), + ('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='plan_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='plan_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')), + ], + options={ + 'verbose_name': '审核计划', + 'verbose_name_plural': '审核计划', + }, + ), + migrations.AddField( + model_name='project', + name='plan', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='project.Plan', verbose_name='所属计划'), + ), + ] diff --git a/server/apps/project/migrations/0026_merge_20200825_1636.py b/server/apps/project/migrations/0026_merge_20200825_1636.py new file mode 100644 index 0000000..da9b564 --- /dev/null +++ b/server/apps/project/migrations/0026_merge_20200825_1636.py @@ -0,0 +1,14 @@ +# Generated by Django 3.0.7 on 2020-08-25 08:36 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('project', '0022_auto_20200821_1435'), + ('project', '0025_auto_20200821_1541'), + ] + + operations = [ + ] diff --git a/server/apps/project/migrations/0027_auto_20200825_1636.py b/server/apps/project/migrations/0027_auto_20200825_1636.py new file mode 100644 index 0000000..c2ec3a4 --- /dev/null +++ b/server/apps/project/migrations/0027_auto_20200825_1636.py @@ -0,0 +1,30 @@ +# Generated by Django 3.0.7 on 2020-08-25 08:36 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('plan', '0001_initial'), + ('project', '0026_merge_20200825_1636'), + ] + + operations = [ + migrations.AddField( + model_name='certapp', + name='accept_by', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='受理人'), + ), + migrations.AlterField( + model_name='project', + name='plan', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='plan.Plan', verbose_name='所属计划'), + ), + migrations.DeleteModel( + name='Plan', + ), + ] diff --git a/server/apps/project/migrations/0032_merge_20200903_1142.py b/server/apps/project/migrations/0032_merge_20200903_1142.py new file mode 100644 index 0000000..17cc6f2 --- /dev/null +++ b/server/apps/project/migrations/0032_merge_20200903_1142.py @@ -0,0 +1,14 @@ +# Generated by Django 3.0.5 on 2020-09-03 03:42 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('project', '0031_auto_20200901_1525'), + ('project', '0027_auto_20200825_1636'), + ] + + operations = [ + ] diff --git a/server/apps/project/models.py b/server/apps/project/models.py index a493262..795084e 100644 --- a/server/apps/project/models.py +++ b/server/apps/project/models.py @@ -4,9 +4,10 @@ from django.db import models from rest_framework.exceptions import ParseError from simple_history.models import HistoricalRecords -from apps.certset.models import ImplementRule, UnitType, Evaluations +from apps.certset.models import ImplementRule, UnitType, Evaluations, Standard from apps.crm.models import Enterprise from apps.system.models import CommonAModel, CommonBModel, Dict, User +# from apps.plan.models import Plan # Create your models here. @@ -14,7 +15,31 @@ class Project(CommonBModel): """ 认证项目 """ - pass + status_choices = ( + ('待下达', '待下达'), + ('待派差', '待派差'), + ('进行中', '进行中'), + ('已中止', '已中止'), + ('已完成', '已完成') + ) + status = models.CharField('项目状态', choices=status_choices, default='待下达', max_length=50) + number = models.CharField('项目编号', max_length = 100, null=True, blank=True) + auditee = models.ForeignKey(Enterprise, related_name='project_auditee', on_delete=models.DO_NOTHING, verbose_name='受审核方') + auditee_v = JSONField(verbose_name='受审核方', default=dict) + remark = models.TextField('备注', null=True, blank=True) + assign_date = models.DateField('下达日期', null=True, blank=True) + assign_by = models.ForeignKey(User, verbose_name='下达人', on_delete=models.SET_NULL, null=True, blank=True) + can_paichai = models.BooleanField('是否可派差', default = False) + plan = models.ForeignKey('plan.Plan', verbose_name='所属计划', on_delete=models.SET_NULL, null=True, blank=True) + + + class Meta: + verbose_name = '认证项目' + verbose_name_plural = verbose_name + + def __str__(self): + return self.number + class CertApp(CommonBModel): """ @@ -37,7 +62,7 @@ class CertApp(CommonBModel): number = models.CharField('受理编号', max_length = 100, null=True, blank=True) apply_date = models.DateField('申请日期', null=True, blank=True) accept_date = models.DateField('受理日期', null=True, blank=True) - + accept_by = models.ForeignKey(User, verbose_name='受理人', on_delete=models.SET_NULL, null=True, blank=True) applicant_v = JSONField(verbose_name='申请方') applicant = models.ForeignKey(Enterprise, related_name='certapp_applicant', on_delete=models.DO_NOTHING) @@ -65,6 +90,7 @@ class CertApp(CommonBModel): manufacture_v = JSONField(verbose_name='制造商', null=True) factory = models.ForeignKey(Enterprise, on_delete=models.CASCADE, related_name='certapp_factory', null=True, blank=True) factory_v = JSONField(verbose_name='生产厂', null=True) + scope = models.TextField('认证范围', null=True, blank=True) system_people = models.FloatField('体系相关员工数',null=True, blank=True, default=0) initial_people = models.FloatField('初始人日数',null=True, blank=True, default=0) supervision_people = models.FloatField('监督人日数',null=True, blank=True, default=0) @@ -173,12 +199,12 @@ class Unit(CommonBModel): """ 认证单元,一个单元一张证书 """ - factory = models.ForeignKey(Enterprise, on_delete=models.DO_NOTHING) - factory_v = JSONField(verbose_name='生产厂', blank=True) name = models.CharField('单元名称', max_length=200) description = models.TextField('单元描述', blank=True) - unittype = models.ForeignKey(UnitType, verbose_name='单元类型', on_delete = models.SET_NULL, null=True, blank=True) - subapplication = models.ForeignKey(Application, verbose_name='所属子申请', on_delete = models.CASCADE) + implementrule = models.ForeignKey(ImplementRule, verbose_name='采用规则', on_delete=models.DO_NOTHING) + unittype = models.ForeignKey(UnitType, verbose_name='单元类型', on_delete = models.DO_NOTHING) + standard = models.ForeignKey(Standard, verbose_name='采用标准', on_delete = models.DO_NOTHING) + certapp = models.ForeignKey(CertApp, verbose_name='所属业务', on_delete = models.CASCADE) class Meta: verbose_name = '认证单元' @@ -188,3 +214,10 @@ class Unit(CommonBModel): return self.name + def save(self, *args, **kwargs): + super().save(*args, **kwargs) + obj = self.certapp + objs = Unit.objects.filter(certapp=obj, is_deleted=False).values_list('name', flat=True) + obj.scope = obj.factory_v['address'] + ':' + ';'.join(objs) + obj.save() + diff --git a/server/apps/project/serializers.py b/server/apps/project/serializers.py index db8b8d4..42cb0eb 100644 --- a/server/apps/project/serializers.py +++ b/server/apps/project/serializers.py @@ -1,9 +1,11 @@ +from apps.plan.models import Plan from rest_framework import serializers from .models import * from apps.system.serializers import DictSerializer, UserListSerializer -# from apps.certset.serializers import ImplementRuleSerializer +from apps.certset.serializers import StandardSerializer +from apps.plan.serializers import PlanSerializer class ApplicationCreateSerializer(serializers.ModelSerializer): number = serializers.CharField(required=False) @@ -41,11 +43,55 @@ class CertappSerializer(serializers.ModelSerializer): cccpv_class_ = DictSerializer(source='cccpv_class' , read_only=True) cnas_scopes_ = DictSerializer(source='cnas_scopes', many=True , read_only=True) create_by_ = UserListSerializer(source='create_by', read_only=True) + accept_by_ = UserListSerializer(source='accept_by', read_only=True) class Meta: model = CertApp fields = '__all__' + class EvaluationDetailSerializer(serializers.ModelSerializer): class Meta: model = EvaluationDetail fields = '__all__' + + + @staticmethod + def setup_eager_loading(queryset): + """ Perform necessary eager loading of data. """ + queryset = queryset.select_related('cert_field', 'cccpv_class', 'create_by', 'accept_by') + queryset = queryset.prefetch_related('cnas_scopes',) + return queryset + +class UnitSerializer(serializers.ModelSerializer): + standard_ = StandardSerializer(source='standard', read_only=True) + class Meta: + model = Unit + fields = '__all__' + +class ProjectSerializer(serializers.ModelSerializer): + create_by_ = UserListSerializer(source='create_by', read_only=True) + assign_by_ = UserListSerializer(source='assign_by', read_only=True) + certapps = serializers.SerializerMethodField() + plan_ = PlanSerializer(source='plan', read_only=True) + + class Meta: + model = Project + fields = '__all__' + + def get_certapps(self, obj): + certapps = [] + for i in CertApp.objects.filter(is_deleted=False, project=obj): + certapps.append(i.cert_field.code +'(' + i.cccpv_class.name +')') + return certapps + + @staticmethod + def setup_eager_loading(queryset): + """ Perform necessary eager loading of data. """ + queryset = queryset.select_related('create_by', 'assign_by', 'plan') + return queryset + +class ProjectUpdateSerializer(serializers.ModelSerializer): + + class Meta: + model = Project + fields = ['remark', 'can_paichai'] diff --git a/server/apps/project/urls.py b/server/apps/project/urls.py index 3a499ed..026c2cd 100644 --- a/server/apps/project/urls.py +++ b/server/apps/project/urls.py @@ -6,7 +6,11 @@ router = routers.DefaultRouter() router.register('application', ApplicationViewSet, basename="application") router.register('subapplication', SubApplicationViewSet, basename="subapplication") router.register('certapp', CertappViewset, basename="certapp") + router.register('evaluationdetail', EvaluationDetailViewset, basename="evaluationdetail") + +router.register('unit', UnitViewSet, basename="unit") +router.register('project', ProjectViewSet, basename="project") urlpatterns = [ path('', include(router.urls)) diff --git a/server/apps/project/views.py b/server/apps/project/views.py index fd18599..7169578 100644 --- a/server/apps/project/views.py +++ b/server/apps/project/views.py @@ -1,5 +1,7 @@ +from rest_framework.views import APIView +from apps import project from django.shortcuts import render -import rest_framework +from rest_framework.serializers import ModelSerializer from rest_framework.viewsets import ModelViewSet, GenericViewSet from rest_framework.response import Response from rest_framework import status @@ -9,9 +11,12 @@ from apps.system.models import Dict from apps.system.permission_data import RbacFilterSet from apps.system.mixins import CreateUpdateCustomMixin, OptimizationMixin import random - +from rest_framework.decorators import action +from .filters import * +from utils.pagination import PageOrNot +from rest_framework.exceptions import ParseError, NotAuthenticated # Create your views here. -class ApplicationViewSet(CreateUpdateCustomMixin, ModelViewSet): +class ApplicationViewSet(RbacFilterSet, ModelViewSet): """ 认证申请 """ @@ -29,7 +34,7 @@ class ApplicationViewSet(CreateUpdateCustomMixin, ModelViewSet): def perform_create(self, serializer): serializer.save(create_by = self.request.user, belong_dept=self.request.user.dept, number=random.randrange(1000,2000)) -class SubApplicationViewSet(CreateUpdateCustomMixin, ModelViewSet): +class SubApplicationViewSet(RbacFilterSet, ModelViewSet): """ 子认证申请 """ @@ -60,7 +65,7 @@ class SubApplicationViewSet(CreateUpdateCustomMixin, ModelViewSet): return None return self.paginator.paginate_queryset(queryset, self.request, view=self) -class CertappViewset(CreateUpdateCustomMixin, ModelViewSet): +class CertappViewset(PageOrNot, RbacFilterSet, ModelViewSet): """ 申请受理 """ @@ -68,18 +73,21 @@ class CertappViewset(CreateUpdateCustomMixin, ModelViewSet): queryset = CertApp.objects.all() serializer_class = CertappSerializer ordering = ['-create_time'] - + filterset_fields = ['status', 'project'] + # filterset_class = CertAppFilter + + def create(self, request, *args, **kwargs): postdata = request.data postdata['number'] = random.randrange(1000,2000) if postdata.get('field_code', None): postdata['cert_field'] = Dict.objects.get(code=postdata['field_code']).id - print(postdata) serializer = self.get_serializer(data=postdata) serializer.is_valid(raise_exception=True) self.perform_create(serializer) headers = self.get_success_headers(serializer.data) return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) + class EvaluationDetailViewset(CreateUpdateCustomMixin, ModelViewSet): """ 受理信息 @@ -96,3 +104,80 @@ class EvaluationDetailViewset(CreateUpdateCustomMixin, ModelViewSet): return None return self.paginator.paginate_queryset(queryset, self.request, view=self) + + + @action(methods=['put'], detail=True, perms_map={'put':'complete_certapp'}, + url_name='complete_certapp') + def complete(self, request, pk=None): + """ + 完成受理 + """ + obj = self.get_object() + obj.status = '已受理' + obj.save() + return Response(status=status.HTTP_200_OK) + +class UnitViewSet(RbacFilterSet, ModelViewSet): + """ + 产品单元 + """ + perms_map = {'get': 'certapp_view', 'post':'certapp_create', 'put':'certapp_update','delete': 'certapp_delete'} + queryset = Unit.objects.all() + serializer_class = UnitSerializer + ordering = ['pk'] + filterset_fields = ['certapp'] + + + +class ProjectViewSet(RbacFilterSet, ModelViewSet): + perms_map = {'get': 'project_view', 'post':'project_create', 'put':'project_update','delete': 'project_delete'} + queryset = Project.objects.all() + serializer_class = ProjectSerializer + ordering = ['pk'] + filterset_class = ProjectFilter + + def get_serializer_class(self): + if self.action == 'update': + return ProjectUpdateSerializer + return ProjectSerializer + + def create(self, request, *args, **kwargs): + postdata = request.data + postdata['number'] = random.randrange(8000,9000) + serializer = self.get_serializer(data=postdata) + serializer.is_valid(raise_exception=True) + if self.request.user is not None: + instance = serializer.save(create_by = self.request.user, belong_dept=self.request.user.dept) + if 'certapps' in postdata and postdata['certapps']: + CertApp.objects.filter(pk__in = postdata['certapps']).update(project=instance, status='进行中') + headers = self.get_success_headers(serializer.data) + return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) + else: + return NotAuthenticated() + + @action(methods=['put'], detail=True, perms_map={'put':'project_assgin'}, + url_name='project_assgin') + def assgin(self, request, pk=None): + """ + 下达项目 + """ + obj = self.get_object() + if obj.status == '待下达': + obj.status = '待派差' + obj.assgin_by = request.user + obj.save() + return Response(status=status.HTTP_200_OK) + else: + return Response('状态有误', status=status.HTTP_400_BAD_REQUEST) + + @action(methods=['put'], detail=False, perms_map={'put':'project_plan'}, + url_name='project_plan') + def plan(self, request, *args, **kwargs): + """ + 项目计划 + """ + projects = request.data['projects'] + plan = request.data['plan'] + if projects: + Project.objects.filter(pk__in=projects).update(plan=plan) + return Response(status=status.HTTP_200_OK) diff --git a/server/apps/system/permission_data.py b/server/apps/system/permission_data.py index 8b7d20f..c418be2 100644 --- a/server/apps/system/permission_data.py +++ b/server/apps/system/permission_data.py @@ -1,11 +1,11 @@ from django.db.models import Q from django.db.models.query import QuerySet from rest_framework.generics import GenericAPIView -from apps.system.mixins import CreateUpdateModelBMixin +from apps.system.mixins import CreateUpdateModelBMixin, CreateUpdateCustomMixin from utils.queryset import get_child_queryset2 -class RbacFilterSet(CreateUpdateModelBMixin, object): +class RbacFilterSet(CreateUpdateCustomMixin, object): """ 数据权限控权返回的queryset 在必须的View下继承 @@ -21,8 +21,6 @@ class RbacFilterSet(CreateUpdateModelBMixin, object): ) queryset = self.queryset - if hasattr(self.get_serializer_class(), 'setup_eager_loading'): - queryset = self.get_serializer_class().setup_eager_loading(queryset) # 性能优化 if isinstance(queryset, QuerySet): # Ensure queryset is re-evaluated on each request. queryset = queryset.all() @@ -50,9 +48,9 @@ class RbacFilterSet(CreateUpdateModelBMixin, object): return queryset elif '仅本人' in data_range: queryset = queryset.filter(Q(create_by=user)|Q(update_by=user)) - return queryset + return queryset if hasattr(self.get_serializer_class(), 'setup_eager_loading'): - queryset = self.get_serializer_class().setup_eager_loading(queryset) # 性能优化 + queryset = self.get_serializer_class().setup_eager_loading(queryset) # 性能优化 return queryset diff --git a/server/server/settings.py b/server/server/settings.py index f857ef6..100cc7d 100644 --- a/server/server/settings.py +++ b/server/server/settings.py @@ -50,7 +50,8 @@ INSTALLED_APPS = [ 'apps.employee', 'apps.project', 'apps.financial', - 'apps.laboratory' + 'apps.laboratory', + 'apps.plan' ] MIDDLEWARE = [ diff --git a/server/server/urls.py b/server/server/urls.py index 8c2af47..dd39de5 100644 --- a/server/server/urls.py +++ b/server/server/urls.py @@ -37,6 +37,7 @@ urlpatterns = [ path('employee/', include('apps.employee.urls')), path('project/', include('apps.project.urls')), path('financial/', include('apps.financial.urls')), + path('plan/', include('apps.plan.urls')), path('laboratory/', include('apps.laboratory.urls')), path('docs/', include_docs_urls(title="接口文档", authentication_classes=[], permission_classes=[])), diff --git a/server/utils/pagination.py b/server/utils/pagination.py index 72e6efa..675d8a8 100644 --- a/server/utils/pagination.py +++ b/server/utils/pagination.py @@ -1,6 +1,16 @@ from rest_framework.pagination import PageNumberPagination - +from rest_framework.exceptions import ParseError class MyPagination(PageNumberPagination): page_size = 10 page_size_query_param = 'page_size' + +class PageOrNot: + def paginate_queryset(self, queryset): + if (self.paginator is None): + return None + elif self.request.query_params.get('pageoff', None) and self.get_queryset().count()<500: + return None + elif self.request.query_params.get('pageoff', None) and self.get_queryset().count()>=500: + return ParseError('单次请求数据量大,请求中止') + return self.paginator.paginate_queryset(queryset, self.request, view=self) \ No newline at end of file