feat: rpj member 后入厂和提前离厂

This commit is contained in:
caoqianming 2023-03-23 17:42:44 +08:00
parent 1f8963514f
commit a4c54c48e0
8 changed files with 262 additions and 69 deletions

View File

@ -0,0 +1,25 @@
# Generated by Django 3.2.12 on 2023-03-23 09:40
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('third', '0007_tdevice_mtask_uid'),
('ecm', '0008_alter_algochannel_always_on'),
]
operations = [
migrations.AlterField(
model_name='algochannel',
name='algo',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ac_algo', to='ecm.eventcate', verbose_name='关联算法'),
),
migrations.AlterField(
model_name='algochannel',
name='vchannel',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ac_vchannel', to='third.tdevice', verbose_name='视频通道'),
),
]

View File

@ -0,0 +1,36 @@
# Generated by Django 3.2.12 on 2023-03-23 09:40
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('rpm', '0007_alter_rcertificate_number'),
]
operations = [
migrations.AddField(
model_name='rpjmember',
name='note',
field=models.CharField(blank=True, help_text='后入厂/提前离厂', max_length=20, null=True, verbose_name='备注'),
),
migrations.CreateModel(
name='RpjLog',
fields=[
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, 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='删除标记')),
('operation', models.CharField(help_text='member_come/member_leave', max_length=20, verbose_name='操作类型')),
('reason', models.CharField(blank=True, max_length=100, null=True, verbose_name='原因')),
('remployee', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='rpm.remployee', verbose_name='关联人员')),
('rpj', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='rpm.rpj', verbose_name='关联项目')),
],
options={
'abstract': False,
},
),
]

View File

@ -124,6 +124,7 @@ class Rpjmember(BaseModel):
duty = models.CharField('职责', max_length=20, null=True, blank=True)
is_manager = models.BooleanField('是否项目负责人', default=False)
rcertificates = models.ManyToManyField(Rcertificate, through='rpm.rpjcertificate', blank=True)
note = models.CharField('备注', max_length=20, null=True, blank=True, help_text='后入厂/提前离厂')
class Rpjcertificate(BaseModel):
@ -140,3 +141,15 @@ class Rpjcertificate(BaseModel):
expiration_date = models.DateField('有效期')
review_date = models.DateField('下一次复审日期')
file = models.CharField('文件地址', max_length=1000, null=True, blank=True)
class RpjLog(BaseModel):
"""项目变更日志
Args:
BaseModel (_type_): _description_
"""
rpj = models.ForeignKey(Rpj, verbose_name='关联项目', on_delete=models.CASCADE)
operation = models.CharField('操作类型', max_length=20, help_text='member_come/member_leave')
remployee = models.ForeignKey(Remployee, verbose_name='关联人员', on_delete=models.SET_NULL, null=True, blank=True)
reason = models.CharField('原因', max_length=100, null=True, blank=True)

View File

@ -14,6 +14,8 @@ from apps.third.dahua import dhClient
from apps.third.tapis import dhapis
from apps.utils.tools import check_id_number_e, check_phone_e
from apps.wf.serializers import TicketSimpleSerializer
from apps.rpm.services import rpj_member_come, rpj_certificate_in
from apps.rpm.models import RpjLog
class RpartyCreateUpdateSerializer(CustomModelSerializer):
@ -158,6 +160,11 @@ class RemployeeUpdateSerializer(CustomModelSerializer):
dhClient.request(**dhapis['person_img_upload'], file_path_rela=validated_data['photo'])
return super().update(instance, validated_data)
class RemployeeSimpleSerializer(CustomModelSerializer):
class Meta:
model = Remployee
fields = ['id', 'name', 'photo', 'id_number']
class RemployeeSerializer(CustomModelSerializer):
rparty_name = serializers.CharField(source='rparty.name', read_only=True)
@ -204,10 +211,10 @@ class RpjmemberCreatesSerializer(CustomModelSerializer):
class RpjmemberCreateSerializer(CustomModelSerializer):
rcertificates = serializers.PrimaryKeyRelatedField(label='证书ID', many=True, queryset=Rcertificate.objects.all())
reason = serializers.CharField(label='后入厂原因', required=False, allow_blank=True)
class Meta:
model = Rpjmember
fields = ['remployee', 'rpj', 'duty', 'is_manager', 'rcertificates']
fields = ['remployee', 'rpj', 'duty', 'is_manager', 'rcertificates', 'reason']
def create(self, validated_data):
rpj = validated_data['rpj']
@ -239,7 +246,12 @@ class RpjmemberCreateSerializer(CustomModelSerializer):
if ins.is_manager:
Rpjmember.objects.exclude(id=ins.id).update(is_manager=False)
if rpj.state in [Rpj.RPJ_ENTER, Rpj.RPJ_WORKING]: # 如果是待入厂后添加的
pass
# 需要执行入厂操作
ins.note = '后入厂'
ins.save()
rpj_member_come(ins)
rpj_certificate_in(ins)
RpjLog.objects.create(rpj=rpj, operation='member_come', remployee=remployee, create_by=self.context['request'].user, reason=validated_data.get('reason', ''))
return ins
@ -272,9 +284,15 @@ class RpjmemberUpdateSerializer(CustomModelSerializer):
if ins.is_manager:
Rpjmember.objects.exclude(id=ins.id).update(is_manager=False)
if rpj.state in [Rpj.RPJ_ENTER, Rpj.RPJ_WORKING]: # 如果是待入厂后更改的
pass
rpj_certificate_in(ins)
return ins
class RpjLogSerializer(CustomModelSerializer):
remployee_ = RemployeeSimpleSerializer(source='remployee', read_only=True)
create_by_ = UserSimpleSerializer(source='create_by', read_only=True)
class Meta:
model = RpjLog
fields = '__all__'
class RpjcertificateSerializer(CustomModelSerializer):
file_f = MyFilePathField(source='file', read_only=True)
@ -310,4 +328,4 @@ class RpjfileSerializer(CustomModelSerializer):
class RpjfileUpdateSerializer(CustomModelSerializer):
class Meta:
model = Rpjfile
fields = ['files']
fields = ['files']

View File

@ -8,6 +8,10 @@ from apps.wf.models import Ticket, Transition
from apps.rpm.models import Remployee, Rfile, Rparty, Rpj, Rpjcertificate, Rpjfile, Rpjmember
from django.contrib.auth.hashers import make_password
from rest_framework.exceptions import ParseError
from dateutil import tz
from django.utils import timezone
from datetime import datetime, timedelta
from django.db.models import Max, Min
def sync_to_rep(ep: Employee):
@ -41,68 +45,142 @@ def bind_rpj(ticket: Ticket, transition: Transition, new_ticket_data: dict):
ticket.create_by = rpj.create_by
ticket.save()
def rpj_member_leave(i: Rpjmember):
"""人员离开项目
"""
rpj = i.rpj
rep = i.remployee
ep = rep.employee
# 查找本人除此之外还在的项目
rpj_qs = Rpj.objects.filter(remployees=rep, state__in = [Rpj.RPJ_WORKING, Rpj.RPJ_ENTER]).exclude(id=rpj.id)
if rpj_qs.exists():
leave_time__max = rpj_qs.aggregate(Max('leave_time'))['leave_time__max']
come_time_min = rpj_qs.aggregate(Min('come_time'))['come_time__min']
rpj_ = rpj_qs.order_by('-create_time').first()
rpj_dept = rpj_.rparty.dept
if ep:
ep.belong_dept = rpj_dept
ep.save()
if ep.user:
ep.user.belong_dept = rpj_dept
ep.user.save()
# 同步至大华人员库并下发人脸
HrmService.sync_dahua_employee(ep, ep.photo, come_time_min, leave_time__max)
else:
if ep:
ep.job_state = Employee.JOB_OFF
ep.save()
HrmService.sync_dahua_employee(ep, ep.photo, rpj.come_time, timezone.now() + timedelta(hours=8))
if ep.user:
ep.user.is_active = False
ep.user.save()
def rpj_member_come(i: Rpjmember):
"""人员入厂
"""
# tzinfo = tz.gettz('Asia/Shanghai')
rep = i.remployee
rpj = i.rpj
rpj_dept = rpj.rparty.dept
post = Post.objects.get(code='remployee')
# 尝试找到人员
ep = Employee.objects.get_queryset(all=True).filter(id_number=rep.id_number).first()
ep2 = Employee.objects.get_queryset(all=True).filter(phone=rep.phone).first()
if ep:
pass
elif ep2:
ep = ep2
else:
ep = Employee()
ep.id_number = rep.id_number
ep.name = rep.name
ep.id_number = rep.id_number
ep.gender = get_info_from_id(rep.id_number).get('gender', '')
ep.phone = rep.phone
old_photo = ep.photo
ep.photo = rep.photo
ep.job_state = Employee.JOB_ON
ep.type = 'remployee'
ep.belong_dept = rpj.rparty.dept
ep.is_deleted = False
ep.job_state = 10
ep.save()
# 给相关方人员创建账户
user_e = ep.user
if user_e: # 如果该人员有账户
pass
else:
user_e = User.objects.get_queryset(all=True).filter(phone=rep.phone).first() # 看看有没有存在的账户
if user_e:
user_e.is_active = True
user_e.is_deleted = False
else:
user_e = User()
user_e.username = 'RE_' + ranstr(6)
user_e.password = make_password('0000')
user_e.name = rep.name
user_e.phone = rep.phone
user_e.type = 'remployee'
user_e.belong_dept = rpj_dept
user_e.post = post
user_e.save()
# 账号划给部门
UserPost.objects.get_or_create(user=user_e, dept=rpj_dept,
defaults={
'user': user_e,
'dept': rpj_dept,
'post': post
})
ep.user = user_e
ep.save()
# 回写
rep.employee = ep
rep.rpj = rpj # 更新当前入厂项目
rep.save()
# come_time = rpj.come_time
# leave_time = rpj.leave_time
# dh_face_card_start = ep.third_info.get('dh_face_card_start', None)
# dh_face_card_end = ep.third_info.get('dh_face_card_end', None)
# if dh_face_card_start:
# dh_face_card_start = datetime.strptime(dh_face_card_start, "%Y-%m-%d %H:%M:%S").replace(tzinfo=tzinfo)
# if dh_face_card_start < come_time:
# come_time = dh_face_card_start
# if dh_face_card_end:
# dh_face_card_end = datetime.strptime(dh_face_card_end, "%Y-%m-%d %H:%M:%S").replace(tzinfo=tzinfo)
# if dh_face_card_end > leave_time:
# leave_time = dh_face_card_end
rpj_qs = Rpj.objects.filter(remployees=rep, state__in = [Rpj.RPJ_WORKING, Rpj.RPJ_ENTER])|Rpj.objects.filter(id=rpj.id)
leave_time__max = rpj_qs.aggregate(Max('leave_time'))['leave_time__max']
come_time_min = rpj_qs.aggregate(Min('come_time'))['come_time__min']
# 同步至大华人员库并下发人脸
HrmService.sync_dahua_employee(ep, old_photo, come_time_min, leave_time__max)
def rpj_certificate_in(i: Rpjmember):
for i in Rpjcertificate.objects.filter(rpj_member=i):
ct = Certificate.objects.filter(number=i.number, name=i.name).first()
if ct:
pass
else:
ct = Certificate()
ct.employee = i.rpj_member.remployee.employee
ct.name = i.name
ct.number = i.number
ct.type = i.type
ct.issue_date = i.issue_date
ct.expiration_date = i.expiration_date
ct.review_date = i.review_date
ct.file = i.file
ct.save()
def rpj_audit_end(ticket):
rpj = Rpj.objects.get(ticket=ticket)
rpj_dept = rpj.rparty.dept
post = Post.objects.get(code='remployee')
# 更新入厂项目人员库
for i in Rpjmember.objects.filter(rpj=rpj):
rep = i.remployee
# 尝试找到人员
ep = Employee.objects.get_queryset(all=True).filter(id_number=rep.id_number).first()
ep2 = Employee.objects.get_queryset(all=True).filter(phone=rep.phone).first()
if ep:
pass
elif ep2:
ep = ep2
else:
ep = Employee()
ep.id_number = rep.id_number
ep.name = rep.name
ep.id_number = rep.id_number
ep.gender = get_info_from_id(rep.id_number).get('gender', '')
ep.phone = rep.phone
ep.photo = rep.photo
ep.type = 'remployee'
ep.belong_dept = rpj.rparty.dept
ep.is_deleted = False
ep.job_state = 10
ep.save()
# 给相关方人员创建账户
user_e = ep.user
if user_e: # 如果该人员有账户
pass
else:
user_e = User.objects.get_queryset(all=True).filter(phone=rep.phone).first() # 看看有没有存在的账户
if user_e:
user_e.is_active = True
user_e.is_deleted = False
else:
user_e = User()
user_e.username = 'RE_' + ranstr(6)
user_e.password = make_password('0000')
user_e.name = rep.name
user_e.phone = rep.phone
user_e.type = 'remployee'
user_e.belong_dept = rpj_dept
user_e.post = post
user_e.save()
# 账号划给部门
UserPost.objects.get_or_create(user=user_e, dept=rpj_dept,
defaults={
'user': user_e,
'dept': rpj_dept,
'post': post
})
ep.user = user_e
ep.save()
# 回写
rep.employee = ep
rep.rpj = rpj # 更新当前入厂项目
rep.save()
# 同步至大华人员库并下发人脸
HrmService.sync_dahua_employee(ep, '', rpj.come_time, rpj.leave_time)
rpj_member_come(i)
# 更新入厂项目人员证书库
for i in Rpjcertificate.objects.filter(rpj_member__rpj=rpj):
ct = Certificate.objects.filter(number=i.number, name=i.name).first()

View File

@ -1,4 +1,5 @@
from apps.rpm.views import RcertificateViewSet, RpartyViewSet, RemployeeViewSet, RfileViewSet, RpjViewSet, RpjfileViewSet, RpjmemberViewSet
from apps.rpm.views import (RcertificateViewSet, RpartyViewSet, RemployeeViewSet, RfileViewSet, RpjViewSet,
RpjfileViewSet, RpjmemberViewSet, RpjLogViewSet)
from django.urls import path, include
from rest_framework.routers import DefaultRouter
@ -12,6 +13,7 @@ router.register('rcertificate', RcertificateViewSet, basename='rcertificate')
router.register('rfile', RfileViewSet, basename='rfile')
router.register('rpj', RpjViewSet, basename='rpj')
router.register('rpj_member', RpjmemberViewSet, basename='rpj_member')
router.register('rpj_log', RpjLogViewSet, basename='rpj_log')
router.register('rpj_file', RpjfileViewSet, basename='rpj_file')
urlpatterns = [
path(API_BASE_URL, include(router.urls)),

View File

@ -1,12 +1,12 @@
from apps.rpm.filters import RpjFilterSet
from apps.rpm.models import Rcertificate, Remployee, Rparty, Rfile, Rpjfile, Rpjmember, Rpj
from apps.rpm.models import Rcertificate, Remployee, Rparty, Rfile, Rpjfile, Rpjmember, Rpj, RpjLog
from apps.rpm.serializers import (RcertificateCreateUpdateSerializer,
RcertificateSerializer, RemployeeCreateSerializer, RemployeeSerializer,
RemployeeUpdateSerializer, RpartyAssignSerializer, RpartyCreateUpdateSerializer,
RfileListSerializer, RpartySerializer, RpjListSerializer, RpjfileSerializer,
RpjfileUpdateSerializer, RpjmemberCreateSerializer,
RpjfileUpdateSerializer, RpjmemberCreateSerializer, RpjLogSerializer,
RpjCreateSerializer, RpjUpdateSerializer, RpjmemberSerializer, RpjmemberUpdateSerializer)
from apps.rpm.services import sync_to_rep
from apps.rpm.services import sync_to_rep, rpj_member_leave
from apps.system.models import Dictionary, Post, User, UserPost
from apps.system.serializers import UserCreateSerializer
from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet
@ -224,10 +224,31 @@ class RpjmemberViewSet(CustomModelViewSet):
def destroy(self, request, *args, **kwargs):
obj = self.get_object()
if obj.rpj.state != Rpj.RPJ_CREATE:
raise ParseError('项目非创建状态不可删除')
if obj.rpj.state in [Rpj.RPJ_DONE, Rpj.RPJ_CLOSE]:
raise ParseError('成员在该状态不可删除')
else:
pass
return super().destroy(request, *args, **kwargs)
@action(methods=['post'], detail=True, perms_map={'post': 'rpj.update'})
@transaction.atomic
def leave(self, request, pk=None):
"""提前离厂
提前离厂
"""
obj = self.get_object()
rpj_member_leave(obj)
RpjLog.objects.create(rpj=obj.rpj, operation='member_leave', remployee=obj.remployee, create_by=request.user, reason=request.data.get('reason', ''))
return Response()
class RpjLogViewSet(ListModelMixin, CustomGenericViewSet):
perms_map = {'get': '*'}
queryset = RpjLog.objects.all()
serializer_class = RpjLogSerializer
select_related_fields = ['rpj', 'remployee', 'create_by']
filterset_fields = ['rpj', 'remployee']
ordering = ['-create_time']
class RpjfileViewSet(UpdateModelMixin, DestroyModelMixin, ListModelMixin, CustomGenericViewSet):
perms_map = {'get': '*', 'put': 'rpj.update', 'delete': 'rpj.update'}

View File

@ -29,7 +29,7 @@ DEBUG = conf.DEBUG
ALLOWED_HOSTS = ['*']
SYS_NAME = 'XT_EHS'
SYS_VERSION = '2.00.12'
SYS_VERSION = '2.00.13'
PROJECT_NAME = conf.PROJECT_NAME