feat: 可手动创建打卡记录

This commit is contained in:
caoqianming 2023-11-17 14:55:03 +08:00
parent 286571d302
commit 58d5b06858
6 changed files with 88 additions and 26 deletions

View File

@ -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')

View File

@ -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='班次'),
),
]

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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):
"""
离岗说明