feat: 可手动创建打卡记录
This commit is contained in:
parent
286571d302
commit
58d5b06858
|
@ -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')
|
||||
|
|
|
@ -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='班次'),
|
||||
),
|
||||
]
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
"""
|
||||
离岗说明
|
||||
|
|
Loading…
Reference in New Issue