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): class ClockRecordFilterSet(filters.FilterSet):
start_create = filters.DateTimeFilter(field_name="create_time", lookup_expr='gte') start_create = filters.DateTimeFilter(
end_create = filters.DateTimeFilter(field_name="create_time", lookup_expr='lte') field_name="create_time", lookup_expr='gte')
end_create = filters.DateTimeFilter(
field_name="create_time", lookup_expr='lte')
year = filters.NumberFilter(method='filter_year') year = filters.NumberFilter(method='filter_year')
month = filters.NumberFilter(method='filter_month') month = filters.NumberFilter(method='filter_month')
@ -15,6 +17,7 @@ class ClockRecordFilterSet(filters.FilterSet):
'type': ['exact', 'in'], 'type': ['exact', 'in'],
'employee__type': ['exact', 'in'], 'employee__type': ['exact', 'in'],
'employee__belong_dept': ['exact'], 'employee__belong_dept': ['exact'],
'clock_time': ['gte', 'lte']
} }
# fields = ['employee', 'start_create', 'end_create', 'year', 'month', 'type', 'employee__type', 'employee__belong_dept'] # fields = ['employee', 'start_create', 'end_create', 'year', 'month', 'type', 'employee__type', 'employee__belong_dept']
@ -27,6 +30,7 @@ class ClockRecordFilterSet(filters.FilterSet):
class EmployeeFilterSet(filters.FilterSet): class EmployeeFilterSet(filters.FilterSet):
has_blt = filters.BooleanFilter(method='filter_has_blt') has_blt = filters.BooleanFilter(method='filter_has_blt')
class Meta: class Meta:
model = Employee model = Employee
fields = { fields = {
@ -42,6 +46,7 @@ class EmployeeFilterSet(filters.FilterSet):
else: else:
return queryset.filter(blt=None) return queryset.filter(blt=None)
class NotWorkRemarkFilterSet(filters.FilterSet): class NotWorkRemarkFilterSet(filters.FilterSet):
year = filters.NumberFilter(method='filter_year') year = filters.NumberFilter(method='filter_year')
month = filters.NumberFilter(method='filter_month') 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', '访客'), ('visitor', '访客'),
('driver', '司机') ('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, user = models.OneToOneField(User,
verbose_name='系统账号', verbose_name='系统账号',
related_name='employee', related_name='employee',
@ -32,19 +33,27 @@ class Employee(CommonBModel):
email = models.EmailField('邮箱号', null=True, blank=True) email = models.EmailField('邮箱号', null=True, blank=True)
number = models.CharField('人员编号', max_length=50, null=True, blank=True) number = models.CharField('人员编号', max_length=50, null=True, blank=True)
photo = models.CharField('证件照', max_length=1000, 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='') gender = models.CharField('性别', max_length=10, default='')
signature = models.CharField('签名图片', max_length=200, null=True, blank=True) signature = models.CharField('签名图片', max_length=200, null=True, blank=True)
birthday = models.DateField('出生年月日', null=True, blank=True) birthday = models.DateField('出生年月日', null=True, blank=True)
qualification = models.CharField('学历', max_length=50, null=True, blank=True) qualification = models.CharField(
job_state = models.IntegerField('在职状态', choices=jobstate_choices, default=10) '学历', max_length=50, null=True, blank=True)
job_state = models.IntegerField(
'在职状态', choices=jobstate_choices, default=10)
is_atwork = models.BooleanField('当前在岗', default=False) is_atwork = models.BooleanField('当前在岗', default=False)
show_atwork = models.BooleanField('是否展示在岗状态', default=True) show_atwork = models.BooleanField('是否展示在岗状态', default=True)
last_check_time = models.DateTimeField('打卡时间', null=True, blank=True) last_check_time = models.DateTimeField('打卡时间', null=True, blank=True)
not_work_remark = models.CharField('当前未打卡说明', null=True, blank=True, max_length=200) not_work_remark = models.CharField(
third_info = models.JSONField('三方信息', default=dict, null=False, blank=True) # 主要是定位卡信息 '当前未打卡说明', null=True, blank=True, max_length=200)
post = models.ForeignKey(Post, verbose_name='所属岗位', on_delete=models.SET_NULL, null=True, blank=True) third_info = models.JSONField(
face_data = models.JSONField('人脸数据', null=True, blank=True) # 存储的是字典(模型名:人脸数据) '三方信息', 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: class Meta:
verbose_name = '员工补充信息' verbose_name = '员工补充信息'
verbose_name_plural = verbose_name verbose_name_plural = verbose_name
@ -95,13 +104,22 @@ class ClockRecord(BaseModel):
(E_TYPE_MISS, '缺卡'), (E_TYPE_MISS, '缺卡'),
(E_TYPE_ADD, '加班') (E_TYPE_ADD, '加班')
) )
type = models.PositiveSmallIntegerField('打卡类型', choices=type_choice, default=ClOCK_ON) type = models.PositiveSmallIntegerField(
employee = models.ForeignKey(Employee, verbose_name='对应人员', on_delete=models.CASCADE) '打卡类型', 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) trigger = models.CharField('触发', max_length=20)
detail = models.JSONField('相关记录', default=dict, null=False, blank=True) # 里面主要有对应的ID值 detail = models.JSONField('相关记录', default=dict,
exception_type = models.PositiveSmallIntegerField('异常类型', choices=E_TYPE_CHOISE, null=True, blank=True) null=False, blank=True) # 里面主要有对应的ID值
exception_type = models.PositiveSmallIntegerField(
'异常类型', choices=E_TYPE_CHOISE, null=True, blank=True)
note = models.CharField('备注', max_length=20, default='') note = models.CharField('备注', max_length=20, default='')
class Certificate(CommonAModel): class Certificate(CommonAModel):
""" """
证书 证书
@ -111,7 +129,8 @@ class Certificate(CommonAModel):
(20, '特种设备操作证书'), (20, '特种设备操作证书'),
(30, '安全管理人员证书') (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) name = models.CharField('证书名称', max_length=20)
number = models.CharField('证书编号', max_length=20) number = models.CharField('证书编号', max_length=20)
type = models.PositiveSmallIntegerField('证书类型', default=10) type = models.PositiveSmallIntegerField('证书类型', default=10)

View File

@ -216,6 +216,18 @@ class ClockRecordListSerializer(serializers.ModelSerializer):
fields = '__all__' 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 ClockRecordSimpleSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = ClockRecord model = ClockRecord

View File

@ -319,6 +319,7 @@ class HrmService:
trigger = 'panel' trigger = 'panel'
# 先直接创建记录 # 先直接创建记录
# 这里原先是将create_time 当作clock_time进行处理了。现在有了clock_time字段这里先不动
cr = ClockRecord.objects.filter( cr = ClockRecord.objects.filter(
employee=ep, create_time=s_time_f).first() employee=ep, create_time=s_time_f).first()
if cr: if cr:
@ -327,6 +328,7 @@ class HrmService:
cr = ClockRecord() cr = ClockRecord()
cr.employee = ep cr.employee = ep
cr.create_time = s_time_f cr.create_time = s_time_f
cr.clock_time = s_time_f
cr.type = card_type cr.type = card_type
cr.exception_type = None cr.exception_type = None
cr.trigger = trigger cr.trigger = trigger

View File

@ -18,7 +18,7 @@ from apps.hrm.serializers import (CertificateCreateUpdateSerializer, Certificate
EmployeeCreateUpdateSerializer, EmployeeDetailSerializer, EmployeeImproveSerializer, EmployeeCreateUpdateSerializer, EmployeeDetailSerializer, EmployeeImproveSerializer,
EmployeeNotWorkRemarkSerializer, EmployeeNotWorkRemarkSerializer,
EmployeeSerializer, EmployeeSerializer,
ClockRecordSimpleSerializer, ClockRecordSimpleSerializer, ClockRecordCreateSerializer,
NotWorkRemarkListSerializer, CorrectSerializer) NotWorkRemarkListSerializer, CorrectSerializer)
from apps.hrm.services import HrmService from apps.hrm.services import HrmService
@ -26,6 +26,7 @@ from apps.third.dahua import dhClient
from apps.third.tapis import dhapis from apps.third.tapis import dhapis
from apps.utils.export import export_excel from apps.utils.export import export_excel
from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet
from apps.utils.mixins import BulkCreateModelMixin, BulkDestroyModelMixin
epTypeOptions = {'employee': '正式员工', 'remployee': '相关方', epTypeOptions = {'employee': '正式员工', 'remployee': '相关方',
'visitor': '访客', 'driver': '货车司机'} 'visitor': '访客', 'driver': '货车司机'}
@ -48,7 +49,8 @@ class EmployeeViewSet(CustomModelViewSet):
create_serializer_class = EmployeeCreateUpdateSerializer create_serializer_class = EmployeeCreateUpdateSerializer
update_serializer_class = EmployeeCreateUpdateSerializer update_serializer_class = EmployeeCreateUpdateSerializer
partial_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'] ordering = ['-pk']
# def filter_queryset(self, queryset): # def filter_queryset(self, queryset):
@ -241,7 +243,7 @@ class EmployeeViewSet(CustomModelViewSet):
return Response({'path': export_excel(field_data, data, '人员信息')}) 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'] select_related_fields = ['employee']
search_fields = ['employee__name', 'employee__number', 'employee__phone'] search_fields = ['employee__name', 'employee__number', 'employee__phone']
serializer_class = ClockRecordListSerializer serializer_class = ClockRecordListSerializer
create_serializer_class = ClockRecordCreateSerializer
filterset_class = ClockRecordFilterSet filterset_class = ClockRecordFilterSet
ordering = ['-create_time'] ordering = ['-create_time']
@ -259,7 +262,8 @@ class ClockRecordViewSet(ListModelMixin, CustomGenericViewSet):
"""导出excel """导出excel
导出excel 导出excel
""" """
field_data = ['人员类型', '人员', '编号', '身份证号', '所属部门', '触发形式', '打卡时间', '打卡推测', '异常推测'] field_data = ['人员类型', '人员', '编号', '身份证号',
'所属部门', '触发形式', '打卡时间', '打卡推测', '异常推测']
queryset = self.filter_queryset(self.get_queryset()) queryset = self.filter_queryset(self.get_queryset())
odata = ClockRecordListSerializer(queryset, many=True).data odata = ClockRecordListSerializer(queryset, many=True).data
# 处理数据 # 处理数据
@ -278,7 +282,6 @@ class ClockRecordViewSet(ListModelMixin, CustomGenericViewSet):
) )
return Response({'path': export_excel(field_data, data, '打卡记录')}) return Response({'path': export_excel(field_data, data, '打卡记录')})
@action(methods=['post'], detail=False, perms_map={'post': '*'}, @action(methods=['post'], detail=False, perms_map={'post': '*'},
serializer_class=serializers.Serializer, logging_methods=[]) serializer_class=serializers.Serializer, logging_methods=[])
def dahua(self, request): def dahua(self, request):
@ -295,7 +298,8 @@ class ClockRecordViewSet(ListModelMixin, CustomGenericViewSet):
if res.get('pageData', None): if res.get('pageData', None):
for i in res['pageData']: for i in res['pageData']:
ids.append(i['id']) 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 = {} crs_dict = {}
for i in crs_info: for i in crs_info:
crs_dict[i['detail']['id']] = i crs_dict[i['detail']['id']] = i
@ -305,7 +309,6 @@ class ClockRecordViewSet(ListModelMixin, CustomGenericViewSet):
i['my_info'] = crs_dict[i['id']] i['my_info'] = crs_dict[i['id']]
return Response(res) return Response(res)
# @action(methods=['post'], detail=False, perms_map={'post': '*'}, # @action(methods=['post'], detail=False, perms_map={'post': '*'},
# serializer_class=CorrectSerializer) # serializer_class=CorrectSerializer)
# def correct_swip(self, request, pk=None): # def correct_swip(self, request, pk=None):
@ -341,6 +344,7 @@ class ClockRecordViewSet(ListModelMixin, CustomGenericViewSet):
# i.save() # i.save()
# return Response() # return Response()
class NotWorkRemarkViewSet(ListModelMixin, CustomGenericViewSet): class NotWorkRemarkViewSet(ListModelMixin, CustomGenericViewSet):
""" """
离岗说明 离岗说明