From b250b9b07864cf4e8d96540f624788e5b3b17bab Mon Sep 17 00:00:00 2001 From: TianyangZhang Date: Mon, 22 Dec 2025 13:41:38 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9Ehrm=20--=E4=BA=BA?= =?UTF-8?q?=E5=91=98=E4=BA=A4=E6=8E=A5=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../migrations/0023_empjoin_emppersoninfo.py | 57 ++++++++++++++++++ apps/hrm/models.py | 28 ++++++++- apps/hrm/serializers.py | 59 ++++++++++++++++++- apps/hrm/urls.py | 3 +- apps/hrm/views.py | 24 ++++++-- 5 files changed, 162 insertions(+), 9 deletions(-) create mode 100644 apps/hrm/migrations/0023_empjoin_emppersoninfo.py diff --git a/apps/hrm/migrations/0023_empjoin_emppersoninfo.py b/apps/hrm/migrations/0023_empjoin_emppersoninfo.py new file mode 100644 index 00000000..41e2e607 --- /dev/null +++ b/apps/hrm/migrations/0023_empjoin_emppersoninfo.py @@ -0,0 +1,57 @@ +# Generated by Django 3.2.12 on 2025-12-22 05:40 + +from django.conf import settings +import django.core.validators +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('system', '0006_auto_20241213_1249'), + ('wf', '0006_auto_20251215_1645'), + ('hrm', '0022_empneed'), + ] + + operations = [ + migrations.CreateModel( + name='EmpPersonInfo', + 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='删除标记')), + ('name', models.CharField(max_length=20, verbose_name='姓名')), + ('gender', models.CharField(default='男', max_length=10, verbose_name='性别')), + ('IDcard', models.CharField(max_length=20, verbose_name='身份证号')), + ('phone', models.CharField(blank=True, max_length=20, null=True, validators=[django.core.validators.RegexValidator('^1[3456789]\\d{9}$', '手机号码格式不正确')], verbose_name='手机号')), + ('note', models.TextField(blank=True, null=True, verbose_name='备注')), + ('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='emppersoninfo_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')), + ('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='emppersoninfo_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='EmpJoin', + 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='删除标记')), + ('person', models.JSONField(default=list, verbose_name='人员信息')), + ('join_date', models.DateField(verbose_name='入职日期')), + ('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='empjoin_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')), + ('dept_need', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='system.dept', verbose_name='入职部门')), + ('ticket', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='empjoin_ticket', to='wf.ticket', verbose_name='关联工单')), + ('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='empjoin_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/apps/hrm/models.py b/apps/hrm/models.py index 79f2f379..832b0263 100755 --- a/apps/hrm/models.py +++ b/apps/hrm/models.py @@ -4,8 +4,9 @@ from apps.system.models import Post, User from apps.utils.models import BaseModel, CommonADModel, CommonAModel, CommonBModel from django.utils import timezone from datetime import timedelta +from django.core.validators import RegexValidator - +PHONE_VALIDATOR = RegexValidator(r'^1[3456789]\d{9}$', '手机号码格式不正确') class Employee(CommonBModel): """ TN:员工信息 @@ -220,4 +221,27 @@ class EmpNeed(CommonADModel): education = models.CharField('学历要求', max_length=50, help_text='高中/大专/不限') professional_requirement = models.TextField(verbose_name="相关专业及技能要求", null=True, blank=True) ticket = models.OneToOneField('wf.ticket', verbose_name='关联工单', - on_delete=models.CASCADE, related_name='empneed_ticket', null=True, blank=True) \ No newline at end of file + on_delete=models.CASCADE, related_name='empneed_ticket', null=True, blank=True) + + +class EmpJoin(CommonADModel): + """ + TN:员工入职 + """ + dept_need = models.ForeignKey('system.Dept', verbose_name='入职部门', on_delete=models.CASCADE) + ticket = models.OneToOneField('wf.ticket', verbose_name='关联工单', + on_delete=models.CASCADE, related_name='empjoin_ticket', null=True, blank=True) + person = models.JSONField('人员信息', default=list) + join_date = models.DateField('入职日期') + + +class EmpPersonInfo(CommonADModel): + """ + TN:入职人员信息 + """ + name = models.CharField('姓名', max_length=20) + gender = models.CharField('性别', max_length=10, default='男') + IDcard = models.CharField('身份证号', max_length=20) + phone = models.CharField('手机号', max_length=20,validators=[PHONE_VALIDATOR], null=True, blank=True) + note = models.TextField('备注', null=True, blank=True) + \ No newline at end of file diff --git a/apps/hrm/serializers.py b/apps/hrm/serializers.py index 5e4ed7ca..d989790d 100755 --- a/apps/hrm/serializers.py +++ b/apps/hrm/serializers.py @@ -9,7 +9,7 @@ from django.utils import timezone from apps.utils.serializers import CustomModelSerializer from apps.utils.constants import EXCLUDE_FIELDS from apps.hrm.models import (Certificate, ClockRecord, Employee, - NotWorkRemark, Attendance, Resignation, EmpNeed) + NotWorkRemark, Attendance, Resignation, EmpNeed, EmpJoin, EmpPersonInfo) from apps.system.serializers import DeptSimpleSerializer, UserSimpleSerializer from django.db import transaction from django.core.cache import cache @@ -347,4 +347,59 @@ class EmpNeedSerializer(CustomModelSerializer): ticket_ = TicketSimpleSerializer(source='ticket', read_only=True) class Meta: model = EmpNeed - fields = '__all__' \ No newline at end of file + fields = '__all__' + +class EmpJoinSerializer(CustomModelSerializer): + ticket_ = TicketSimpleSerializer(source='ticket', read_only=True) + class Meta: + model = EmpJoin + fields = '__all__' + +class EmpPersonInfoSerializer(CustomModelSerializer): + class Meta: + model = EmpPersonInfo + fields = ( + 'name', + 'gender', + 'IDcard', + 'phone', + 'note', + ) + +class EmpJoinApproveSerializer(CustomModelSerializer): + empjoin_id = serializers.IntegerField() + + def validate_empjoin_id(self, value): + try: + empjoin = EmpJoin.objects.get(id=value) + except EmpJoin.DoesNotExist: + raise ParseError('该入职申请不存在') + if not isinstance(empjoin.person, list) or not empjoin.person: + raise ParseError('该入职申请未添加人员') + self.empjoin = empjoin + return value + + def validate(self, attrs): + """ + 校验 person 中每一个人员 + """ + self.person_serializers = [] + errors = [] + for index, item in enumerate(self.empjoin.person): + serializer = EmpPersonInfoSerializer(data=item) + if serializer.is_valid(): + self.person_serializers.append(serializer) + else: + errors.append({ + "index": index, + "errors": serializer.errors + }) + if errors: + raise serializers.ValidationError({"person": errors}) + return attrs + + @transaction.atomic + def create(self, validated_data): + objs = [EmpPersonInfo(**serializer.validated_data) for serializer in self.person_serializers] + EmpPersonInfo.objects.bulk_create(objs) + return objs \ No newline at end of file diff --git a/apps/hrm/urls.py b/apps/hrm/urls.py index 863d42cb..5154e3f4 100755 --- a/apps/hrm/urls.py +++ b/apps/hrm/urls.py @@ -1,5 +1,5 @@ from apps.hrm.views import (CertificateViewSet, ClockRecordViewSet, EmployeeViewSet, NotWorkRemarkViewSet, EmpNeedViewSet, - AttendanceViewSet, ResignationViewSet) + AttendanceViewSet, ResignationViewSet, EmpJoinViewSet) from django.urls import path, include from rest_framework.routers import DefaultRouter @@ -15,6 +15,7 @@ router.register('certificate', CertificateViewSet, basename='certificate') router.register('attendance', AttendanceViewSet, basename='attendance') router.register('resignation', ResignationViewSet, basename='resignation') router.register('empneed', EmpNeedViewSet, basename='empneed') +router.register('empjoin', EmpJoinViewSet, basename='empjoin') urlpatterns = [ path(API_BASE_URL, include(router.urls)), ] diff --git a/apps/hrm/views.py b/apps/hrm/views.py index 1d53d061..c2634921 100755 --- a/apps/hrm/views.py +++ b/apps/hrm/views.py @@ -12,15 +12,15 @@ from rest_framework.response import Response from apps.hrm.errors import NO_NEED_LEVEL_REMARK from apps.hrm.filters import (CertificateFilterSet, ClockRecordFilterSet, EmployeeFilterSet, NotWorkRemarkFilterSet) -from apps.hrm.models import Certificate, ClockRecord, Employee, NotWorkRemark, Attendance, Resignation, EmpNeed -from apps.hrm.serializers import (CertificateCreateUpdateSerializer, CertificateSerializer, ChannelAuthoritySerializer, +from apps.hrm.models import Certificate, ClockRecord, Employee, NotWorkRemark, Attendance, Resignation, EmpNeed, EmpJoin +from apps.hrm.serializers import (CertificateCreateUpdateSerializer, CertificateSerializer, ChannelAuthoritySerializer, EmpJoinSerializer, ClockRecordListSerializer, EmployeeCreateUpdateSerializer, EmployeeDetailSerializer, EmployeeImproveSerializer, EmployeeNotWorkRemarkSerializer, EmployeeSerializer, ClockRecordSimpleSerializer, ClockRecordCreateSerializer, NotWorkRemarkListSerializer, CorrectSerializer, AttendanceSerializer, - ResignationSerializer, EmpNeedSerializer) + ResignationSerializer, EmpNeedSerializer, EmpJoinApproveSerializer) from apps.hrm.services import HrmService from apps.third.dahua import dhClient @@ -430,4 +430,20 @@ class EmpNeedViewSet(TicketMixin, EuModelViewSet): workflow_key = "wf_empneed" def gen_other_ticket_data(self, instance): - return {"post_need": instance.post_need} \ No newline at end of file + return {"post_need": instance.post_need} + + +class EmpJoinViewSet(TicketMixin, EuModelViewSet): + queryset = EmpJoin.objects.all() + serializer_class = EmpJoinSerializer + workflow_key = "wf_empjoin" + + @action(methods=['post'], detail=True) + def approve(self, request, pk=None): + serializer = EmpJoinApproveSerializer(data={'empjoin_id': pk}) + serializer.is_valid(raise_exception=True) + person = serializer.save() + return Response({ + "msg":"审批通过,人员入库", + "count": len(person) + }) \ No newline at end of file