diff --git a/apps/hrm/filters.py b/apps/hrm/filters.py index d6c3bc59..c4f28b68 100755 --- a/apps/hrm/filters.py +++ b/apps/hrm/filters.py @@ -3,8 +3,10 @@ from apps.hrm.models import Certificate, ClockRecord, Employee, NotWorkRemark class ClockRecordFilterSet(filters.FilterSet): - start_create = filters.DateTimeFilter(field_name="create_time", lookup_expr='gte') - end_create = filters.DateTimeFilter(field_name="create_time", lookup_expr='lte') + start_create = filters.DateTimeFilter( + field_name="create_time", lookup_expr='gte') + end_create = filters.DateTimeFilter( + field_name="create_time", lookup_expr='lte') year = filters.NumberFilter(method='filter_year') month = filters.NumberFilter(method='filter_month') @@ -15,6 +17,7 @@ class ClockRecordFilterSet(filters.FilterSet): 'type': ['exact', 'in'], 'employee__type': ['exact', 'in'], 'employee__belong_dept': ['exact'], + 'clock_time': ['gte', 'lte'] } # fields = ['employee', 'start_create', 'end_create', 'year', 'month', 'type', 'employee__type', 'employee__belong_dept'] @@ -27,13 +30,14 @@ class ClockRecordFilterSet(filters.FilterSet): class EmployeeFilterSet(filters.FilterSet): has_blt = filters.BooleanFilter(method='filter_has_blt') + class Meta: model = Employee fields = { 'job_state': ['exact'], 'show_atwork': ['exact'], 'type': ['exact', 'in'], - 'belong_dept':['exact'] + 'belong_dept': ['exact'] } def filter_has_blt(self, queryset, name, value): @@ -42,6 +46,7 @@ class EmployeeFilterSet(filters.FilterSet): else: return queryset.filter(blt=None) + class NotWorkRemarkFilterSet(filters.FilterSet): year = filters.NumberFilter(method='filter_year') month = filters.NumberFilter(method='filter_month') diff --git a/apps/hrm/migrations/0016_clockrecord_shift.py b/apps/hrm/migrations/0016_clockrecord_shift.py new file mode 100644 index 00000000..f40b45b0 --- /dev/null +++ b/apps/hrm/migrations/0016_clockrecord_shift.py @@ -0,0 +1,20 @@ +# Generated by Django 3.2.12 on 2023-11-17 06:12 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('mtm', '0024_auto_20231116_1416'), + ('hrm', '0015_clockrecord_note'), + ] + + operations = [ + migrations.AddField( + model_name='clockrecord', + name='shift', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mtm.shift', verbose_name='班次'), + ), + ] diff --git a/apps/hrm/models.py b/apps/hrm/models.py index 03c12439..a361e7a2 100755 --- a/apps/hrm/models.py +++ b/apps/hrm/models.py @@ -22,7 +22,8 @@ class Employee(CommonBModel): ('visitor', '访客'), ('driver', '司机') ) - type = models.CharField('人员类型', default='employee', max_length=10, choices=PEOPLE_TYPE_CHOICES) + type = models.CharField('人员类型', default='employee', + max_length=10, choices=PEOPLE_TYPE_CHOICES) user = models.OneToOneField(User, verbose_name='系统账号', related_name='employee', @@ -32,19 +33,27 @@ class Employee(CommonBModel): email = models.EmailField('邮箱号', null=True, blank=True) number = models.CharField('人员编号', max_length=50, null=True, blank=True) photo = models.CharField('证件照', max_length=1000, null=True, blank=True) - id_number = models.CharField('身份证号', max_length=100, null=True, blank=True, unique=True) + id_number = models.CharField( + '身份证号', max_length=100, null=True, blank=True, unique=True) gender = models.CharField('性别', max_length=10, default='男') signature = models.CharField('签名图片', max_length=200, null=True, blank=True) birthday = models.DateField('出生年月日', null=True, blank=True) - qualification = models.CharField('学历', max_length=50, null=True, blank=True) - job_state = models.IntegerField('在职状态', choices=jobstate_choices, default=10) + qualification = models.CharField( + '学历', max_length=50, null=True, blank=True) + job_state = models.IntegerField( + '在职状态', choices=jobstate_choices, default=10) is_atwork = models.BooleanField('当前在岗', default=False) show_atwork = models.BooleanField('是否展示在岗状态', default=True) last_check_time = models.DateTimeField('打卡时间', null=True, blank=True) - not_work_remark = models.CharField('当前未打卡说明', null=True, blank=True, max_length=200) - third_info = models.JSONField('三方信息', default=dict, null=False, blank=True) # 主要是定位卡信息 - post = models.ForeignKey(Post, verbose_name='所属岗位', on_delete=models.SET_NULL, null=True, blank=True) - face_data = models.JSONField('人脸数据', null=True, blank=True) # 存储的是字典(模型名:人脸数据) + not_work_remark = models.CharField( + '当前未打卡说明', null=True, blank=True, max_length=200) + third_info = models.JSONField( + '三方信息', default=dict, null=False, blank=True) # 主要是定位卡信息 + post = models.ForeignKey(Post, verbose_name='所属岗位', + on_delete=models.SET_NULL, null=True, blank=True) + face_data = models.JSONField( + '人脸数据', null=True, blank=True) # 存储的是字典(模型名:人脸数据) + class Meta: verbose_name = '员工补充信息' verbose_name_plural = verbose_name @@ -95,13 +104,22 @@ class ClockRecord(BaseModel): (E_TYPE_MISS, '缺卡'), (E_TYPE_ADD, '加班') ) - type = models.PositiveSmallIntegerField('打卡类型', choices=type_choice, default=ClOCK_ON) - employee = models.ForeignKey(Employee, verbose_name='对应人员', on_delete=models.CASCADE) + type = models.PositiveSmallIntegerField( + '打卡类型', choices=type_choice, default=ClOCK_ON) + employee = models.ForeignKey( + Employee, verbose_name='对应人员', on_delete=models.CASCADE) + clock_time = models.DateTimeField('打卡时间', null=True, blank=True) + shift = models.ForeignKey('mtm.shift', verbose_name='班次', + on_delete=models.SET_NULL, null=True, blank=True) + # panel, location, door, manual trigger = models.CharField('触发', max_length=20) - detail = models.JSONField('相关记录', default=dict, null=False, blank=True) # 里面主要有对应的ID值 - exception_type = models.PositiveSmallIntegerField('异常类型', choices=E_TYPE_CHOISE, null=True, blank=True) + detail = models.JSONField('相关记录', default=dict, + null=False, blank=True) # 里面主要有对应的ID值 + exception_type = models.PositiveSmallIntegerField( + '异常类型', choices=E_TYPE_CHOISE, null=True, blank=True) note = models.CharField('备注', max_length=20, default='') + class Certificate(CommonAModel): """ 证书 @@ -111,7 +129,8 @@ class Certificate(CommonAModel): (20, '特种设备操作证书'), (30, '安全管理人员证书') ) - employee = models.ForeignKey(Employee, verbose_name='对应人员', on_delete=models.CASCADE) + employee = models.ForeignKey( + Employee, verbose_name='对应人员', on_delete=models.CASCADE) name = models.CharField('证书名称', max_length=20) number = models.CharField('证书编号', max_length=20) type = models.PositiveSmallIntegerField('证书类型', default=10) diff --git a/apps/hrm/serializers.py b/apps/hrm/serializers.py index 317cc287..9a30544c 100755 --- a/apps/hrm/serializers.py +++ b/apps/hrm/serializers.py @@ -216,6 +216,18 @@ class ClockRecordListSerializer(serializers.ModelSerializer): fields = '__all__' +class ClockRecordCreateSerializer(serializers.ModelSerializer): + clock_time = serializers.DateTimeField(label='打卡时间', required=True) + + class Meta: + model = ClockRecord + fields = ['id', 'type', 'employee', 'clock_time', 'shift'] + + def validate(self, attrs): + attrs['trigger'] = 'manual' + return attrs + + class ClockRecordSimpleSerializer(serializers.ModelSerializer): class Meta: model = ClockRecord diff --git a/apps/hrm/services.py b/apps/hrm/services.py index 9efe04ba..10673545 100755 --- a/apps/hrm/services.py +++ b/apps/hrm/services.py @@ -319,6 +319,7 @@ class HrmService: trigger = 'panel' # 先直接创建记录 + # 这里原先是将create_time 当作clock_time进行处理了。现在有了clock_time字段,这里先不动 cr = ClockRecord.objects.filter( employee=ep, create_time=s_time_f).first() if cr: @@ -327,6 +328,7 @@ class HrmService: cr = ClockRecord() cr.employee = ep cr.create_time = s_time_f + cr.clock_time = s_time_f cr.type = card_type cr.exception_type = None cr.trigger = trigger diff --git a/apps/hrm/views.py b/apps/hrm/views.py index 5c1b126f..01e3a4a0 100755 --- a/apps/hrm/views.py +++ b/apps/hrm/views.py @@ -18,7 +18,7 @@ from apps.hrm.serializers import (CertificateCreateUpdateSerializer, Certificate EmployeeCreateUpdateSerializer, EmployeeDetailSerializer, EmployeeImproveSerializer, EmployeeNotWorkRemarkSerializer, EmployeeSerializer, - ClockRecordSimpleSerializer, + ClockRecordSimpleSerializer, ClockRecordCreateSerializer, NotWorkRemarkListSerializer, CorrectSerializer) from apps.hrm.services import HrmService @@ -26,6 +26,7 @@ from apps.third.dahua import dhClient from apps.third.tapis import dhapis from apps.utils.export import export_excel from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet +from apps.utils.mixins import BulkCreateModelMixin, BulkDestroyModelMixin epTypeOptions = {'employee': '正式员工', 'remployee': '相关方', 'visitor': '访客', 'driver': '货车司机'} @@ -48,7 +49,8 @@ class EmployeeViewSet(CustomModelViewSet): create_serializer_class = EmployeeCreateUpdateSerializer update_serializer_class = EmployeeCreateUpdateSerializer partial_update_serializer_class = EmployeeCreateUpdateSerializer - search_fields = ['name', 'number', 'user__username', 'id_number', 'id', 'phone'] + search_fields = ['name', 'number', + 'user__username', 'id_number', 'id', 'phone'] ordering = ['-pk'] # def filter_queryset(self, queryset): @@ -241,7 +243,7 @@ class EmployeeViewSet(CustomModelViewSet): return Response({'path': export_excel(field_data, data, '人员信息')}) -class ClockRecordViewSet(ListModelMixin, CustomGenericViewSet): +class ClockRecordViewSet(BulkCreateModelMixin, ListModelMixin, BulkDestroyModelMixin, CustomGenericViewSet): """ 打卡记录 """ @@ -250,6 +252,7 @@ class ClockRecordViewSet(ListModelMixin, CustomGenericViewSet): select_related_fields = ['employee'] search_fields = ['employee__name', 'employee__number', 'employee__phone'] serializer_class = ClockRecordListSerializer + create_serializer_class = ClockRecordCreateSerializer filterset_class = ClockRecordFilterSet ordering = ['-create_time'] @@ -259,7 +262,8 @@ class ClockRecordViewSet(ListModelMixin, CustomGenericViewSet): """导出excel 导出excel """ - field_data = ['人员类型', '人员', '编号', '身份证号', '所属部门', '触发形式', '打卡时间', '打卡推测', '异常推测'] + field_data = ['人员类型', '人员', '编号', '身份证号', + '所属部门', '触发形式', '打卡时间', '打卡推测', '异常推测'] queryset = self.filter_queryset(self.get_queryset()) odata = ClockRecordListSerializer(queryset, many=True).data # 处理数据 @@ -278,7 +282,6 @@ class ClockRecordViewSet(ListModelMixin, CustomGenericViewSet): ) return Response({'path': export_excel(field_data, data, '打卡记录')}) - @action(methods=['post'], detail=False, perms_map={'post': '*'}, serializer_class=serializers.Serializer, logging_methods=[]) def dahua(self, request): @@ -288,14 +291,15 @@ class ClockRecordViewSet(ListModelMixin, CustomGenericViewSet): 大华刷脸分页带my_info """ request.data.update({ - "openType":"61", - }) + "openType": "61", + }) _, res = dhClient.request(**dhapis['swipe_list'], json=request.data) ids = [] if res.get('pageData', None): for i in res['pageData']: ids.append(i['id']) - crs_info = ClockRecordSimpleSerializer(instance=ClockRecord.objects.filter(detail__id__in=ids), many=True).data + crs_info = ClockRecordSimpleSerializer( + instance=ClockRecord.objects.filter(detail__id__in=ids), many=True).data crs_dict = {} for i in crs_info: crs_dict[i['detail']['id']] = i @@ -305,7 +309,6 @@ class ClockRecordViewSet(ListModelMixin, CustomGenericViewSet): i['my_info'] = crs_dict[i['id']] return Response(res) - # @action(methods=['post'], detail=False, perms_map={'post': '*'}, # serializer_class=CorrectSerializer) # def correct_swip(self, request, pk=None): @@ -334,13 +337,14 @@ class ClockRecordViewSet(ListModelMixin, CustomGenericViewSet): # sr.is_valid(raise_exception=True) # vdata = sr.validated_data # for i in DrfRequestLog.objects.filter(path='/api/third/dahua/c_swip/', data__contains='办公楼考勤面板' - # , create_time__gte=vdata['start_time'] + # , create_time__gte=vdata['start_time'] # , create_time__lte=vdata['end_time']).filter(data__contains = "'enterOrExit': 1" ): # data = i.data # i.data = data.replace("'enterOrExit': 1", "'enterOrExit': 3") # i.save() # return Response() + class NotWorkRemarkViewSet(ListModelMixin, CustomGenericViewSet): """ 离岗说明