Merge branch 'master' of http://gitea.xxhhcty.xyz:8080/zcdsj/factory
This commit is contained in:
commit
c99e74eee8
|
|
@ -0,0 +1,103 @@
|
|||
# Generated by Django 3.2.12 on 2026-02-05 05:37
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('hrm', '0028_auto_20260126_1445'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='address',
|
||||
field=models.CharField(blank=True, max_length=100, null=True, verbose_name='现住址'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='birthpart',
|
||||
field=models.CharField(blank=True, max_length=40, null=True, verbose_name='出生地'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='birthplace',
|
||||
field=models.CharField(blank=True, max_length=40, null=True, verbose_name='籍贯'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='card_type',
|
||||
field=models.CharField(blank=True, max_length=10, null=True, verbose_name='证件类型'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='categories',
|
||||
field=models.CharField(blank=True, max_length=20, null=True, verbose_name='员工类别'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='company',
|
||||
field=models.CharField(blank=True, max_length=30, null=True, verbose_name='所属公司'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='country',
|
||||
field=models.CharField(blank=True, max_length=20, null=True, verbose_name='国籍'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='health',
|
||||
field=models.CharField(blank=True, max_length=10, null=True, verbose_name='健康状况'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='join_job',
|
||||
field=models.DateField(blank=True, null=True, verbose_name='参加工作时间'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='join_partisan_date',
|
||||
field=models.DateField(blank=True, null=True, verbose_name='入党时间'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='join_system',
|
||||
field=models.DateField(blank=True, null=True, verbose_name='入职系统时间'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='marriage',
|
||||
field=models.CharField(blank=True, max_length=5, null=True, verbose_name='婚姻状况'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='nation',
|
||||
field=models.CharField(blank=True, max_length=20, null=True, verbose_name='民族'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='office_date',
|
||||
field=models.DateField(blank=True, null=True, verbose_name='任职时间'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='partisan',
|
||||
field=models.CharField(blank=True, max_length=20, null=True, verbose_name='政治面貌'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='position',
|
||||
field=models.CharField(blank=True, max_length=20, null=True, verbose_name='职务'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='rank',
|
||||
field=models.CharField(blank=True, max_length=20, null=True, verbose_name='职级'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='record_data',
|
||||
field=models.DateField(blank=True, null=True, verbose_name='录入日期'),
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
# Generated by Django 3.2.12 on 2026-02-09 07:59
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('system', '0007_alter_dept_create_by_alter_dept_third_info_and_more'),
|
||||
('wf', '0006_auto_20251215_1645'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('hrm', '0029_auto_20260205_1337'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Probation',
|
||||
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='删除标记')),
|
||||
('trial_date', models.DateField(verbose_name='试用日期')),
|
||||
('regular_date', models.DateField(verbose_name='转正日期')),
|
||||
('content', models.TextField(blank=True, null=True, verbose_name='个人工作内容')),
|
||||
('record_file', models.CharField(blank=True, max_length=100, null=True, verbose_name='应聘人员登记表')),
|
||||
('apply_file', models.CharField(blank=True, max_length=100, null=True, verbose_name='员工转正申请表')),
|
||||
('ppt_file', models.CharField(blank=True, max_length=100, null=True, verbose_name='PPT 汇报考核')),
|
||||
('belong_dept', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='probation_belong_dept', to='system.dept', verbose_name='所属部门')),
|
||||
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='probation_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
|
||||
('empperson', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='hrm.emppersoninfo', verbose_name='人员信息')),
|
||||
('reg_dept', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='system.dept', verbose_name='转正部门')),
|
||||
('reg_post', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='system.post', verbose_name='转正岗位')),
|
||||
('ticket', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='probation_ticket', to='wf.ticket', verbose_name='关联工单')),
|
||||
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='probation_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
# Generated by Django 3.2.12 on 2026-02-27 01:33
|
||||
|
||||
from django.conf import settings
|
||||
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),
|
||||
('wf', '0006_auto_20251215_1645'),
|
||||
('hrm', '0030_probation'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='end_contract_date',
|
||||
field=models.DateField(blank=True, null=True, verbose_name='合同到期日期'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='EmpContract',
|
||||
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='删除标记')),
|
||||
('counts', models.PositiveSmallIntegerField(default=1, verbose_name='合同变更次数')),
|
||||
('plan_renewal', models.DateField(blank=True, null=True, verbose_name='应续签')),
|
||||
('normal_renewal', models.DateField(blank=True, null=True, verbose_name='正常续签')),
|
||||
('change_date', models.IntegerField(blank=True, null=True, verbose_name='续签/变更(年)')),
|
||||
('change_reason', 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='empcontract_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
|
||||
('employee', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='hrm.employee', verbose_name='人员信息')),
|
||||
('ticket', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='contract_ticket', to='wf.ticket', verbose_name='关联工单')),
|
||||
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='empcontract_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,207 @@
|
|||
# Generated by Django 3.2.12 on 2026-03-02 07:30
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('hrm', '0031_auto_20260227_0933'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='employee',
|
||||
name='birthpart',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='employee',
|
||||
name='card_type',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='employee',
|
||||
name='categories',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='employee',
|
||||
name='company',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='employee',
|
||||
name='country',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='employee',
|
||||
name='end_contract_date',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='employee',
|
||||
name='health',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='employee',
|
||||
name='join_job',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='employee',
|
||||
name='join_system',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='employee',
|
||||
name='rank',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='employee',
|
||||
name='record_data',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='age',
|
||||
field=models.PositiveIntegerField(blank=True, null=True, verbose_name='年龄'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='band_card',
|
||||
field=models.CharField(blank=True, max_length=30, null=True, verbose_name='银行卡号'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='contract_end_date',
|
||||
field=models.DateField(blank=True, null=True, verbose_name='合同到期日'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='emergency_contact',
|
||||
field=models.CharField(blank=True, max_length=20, null=True, verbose_name='紧急联系人姓名'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='emergency_phone',
|
||||
field=models.CharField(blank=True, max_length=20, null=True, verbose_name='紧急联系人电话'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='full_edu',
|
||||
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='全日制最高学历'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='full_edu_major',
|
||||
field=models.CharField(blank=True, max_length=100, null=True, verbose_name='全日制最高学历专业'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='full_edu_school',
|
||||
field=models.CharField(blank=True, max_length=100, null=True, verbose_name='全日制最高学历学校名称'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='full_edu_time',
|
||||
field=models.DateField(blank=True, null=True, verbose_name='全日制最高学历毕业时间'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='honor',
|
||||
field=models.TextField(blank=True, null=True, verbose_name='所获荣誉'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='hukou_address',
|
||||
field=models.CharField(blank=True, max_length=100, null=True, verbose_name='户籍地址'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='hukou_type',
|
||||
field=models.CharField(blank=True, max_length=20, null=True, verbose_name='户口性质'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='part_edu',
|
||||
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='非全日制最高学历'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='part_edu_major',
|
||||
field=models.CharField(blank=True, max_length=100, null=True, verbose_name='非全日制最高学历专业'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='part_edu_school',
|
||||
field=models.CharField(blank=True, max_length=100, null=True, verbose_name='非全日制最高学历学校名称'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='part_edu_time',
|
||||
field=models.DateField(blank=True, null=True, verbose_name='非全日制最高学历毕业时间'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='regular_date',
|
||||
field=models.DateField(blank=True, null=True, verbose_name='转正日期'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='skill_rank',
|
||||
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='职业技能等级'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='skill_rank_date',
|
||||
field=models.DateField(blank=True, null=True, verbose_name='获得技能等级证书日期'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='work_years',
|
||||
field=models.PositiveIntegerField(blank=True, null=True, verbose_name='工龄'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='workshop',
|
||||
field=models.CharField(blank=True, max_length=20, null=True, verbose_name='车间'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='zhuanzhi',
|
||||
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='枣庄市职称'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='zhuanzhi_date',
|
||||
field=models.DateField(blank=True, null=True, verbose_name='获得枣庄市职称日期'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='zhuanzhi_img_1',
|
||||
field=models.CharField(blank=True, max_length=100, null=True, verbose_name='职称证书图片1'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='zhuanzhi_img_2',
|
||||
field=models.CharField(blank=True, max_length=100, null=True, verbose_name='职称证书图片2'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='zyjt_zhuanzhi',
|
||||
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='总院/集团职称'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='zyjt_zhuanzhi_date',
|
||||
field=models.DateField(blank=True, null=True, verbose_name='获得职称日期'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='zyjt_zhuanzhi_img_1',
|
||||
field=models.CharField(blank=True, max_length=100, null=True, verbose_name='总院/集团职称证书图片1'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='employee',
|
||||
name='address',
|
||||
field=models.CharField(blank=True, max_length=100, null=True, verbose_name='现住地址'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='employee',
|
||||
name='office_date',
|
||||
field=models.DateField(blank=True, null=True, verbose_name='职务聘任日期'),
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
# Generated by Django 3.2.12 on 2026-03-03 01:56
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('hrm', '0032_auto_20260302_1530'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='first_social_security_date',
|
||||
field=models.DateField(blank=True, null=True, verbose_name='首次缴纳社保日期'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='is_veteran',
|
||||
field=models.BooleanField(default=False, verbose_name='是否为退役军人'),
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
# Generated by Django 3.2.12 on 2026-03-03 03:07
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('hrm', '0033_auto_20260303_0956'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='employee',
|
||||
name='full_edu_time',
|
||||
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='全日制最高学历毕业时间'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='employee',
|
||||
name='part_edu_time',
|
||||
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='非全日制最高学历毕业时间'),
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 3.2.12 on 2026-03-03 03:26
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('hrm', '0034_auto_20260303_1107'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name='employee',
|
||||
old_name='band_card',
|
||||
new_name='bank_card',
|
||||
),
|
||||
]
|
||||
|
|
@ -25,8 +25,7 @@ class Employee(CommonBModel):
|
|||
('visitor', '访客'),
|
||||
('driver', '司机')
|
||||
)
|
||||
type = models.CharField('人员类型', default='employee',
|
||||
max_length=10, choices=PEOPLE_TYPE_CHOICES)
|
||||
|
||||
user = models.OneToOneField(User,
|
||||
verbose_name='系统账号',
|
||||
related_name='employee',
|
||||
|
|
@ -36,11 +35,8 @@ 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)
|
||||
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(
|
||||
|
|
@ -56,8 +52,52 @@ class Employee(CommonBModel):
|
|||
on_delete=models.SET_NULL, null=True, blank=True)
|
||||
face_data = models.JSONField(
|
||||
'人脸数据', null=True, blank=True) # 存储的是字典(模型名:人脸数据)
|
||||
start_date = models.DateField('入职日期', null=True, blank=True)
|
||||
end_date = models.DateField('离职日期', null=True, blank=True)
|
||||
workshop = models.CharField('车间', max_length=20, null=True, blank=True)
|
||||
position = models.CharField('职务', max_length=20, null=True, blank=True)
|
||||
office_date = models.DateField('职务聘任日期', null=True, blank=True)
|
||||
type = models.CharField('人员类型', default='employee',
|
||||
max_length=10, choices=PEOPLE_TYPE_CHOICES)
|
||||
gender = models.CharField('性别', max_length=10, default='男')
|
||||
id_number = models.CharField(
|
||||
'身份证号', max_length=100, null=True, blank=True, unique=True)
|
||||
bank_card = models.CharField('银行卡号', max_length=30, null=True, blank=True)
|
||||
start_date = models.DateField('入职日期', null=True, blank=True)
|
||||
contract_end_date = models.DateField('合同到期日', null=True, blank=True)
|
||||
regular_date = models.DateField('转正日期', null=True, blank=True)
|
||||
birthday = models.DateField('出生年月日', null=True, blank=True)
|
||||
age = models.PositiveIntegerField('年龄', null=True, blank=True)
|
||||
work_years = models.PositiveIntegerField('工龄', null=True, blank=True)
|
||||
partisan = models.CharField('政治面貌', max_length=20, null=True, blank=True)
|
||||
join_partisan_date = models.DateField('入党时间', null=True, blank=True)
|
||||
nation = models.CharField('民族', max_length=20, null=True, blank=True)
|
||||
marriage = models.CharField('婚姻状况', max_length=5, null=True, blank=True)
|
||||
hukou_type = models.CharField('户口性质', max_length=20, null=True, blank=True)
|
||||
birthplace = models.CharField('籍贯', max_length=40, null=True, blank=True)
|
||||
hukou_address = models.CharField('户籍地址', max_length=100, null=True, blank=True)
|
||||
address = models.CharField('现住地址', max_length=100, null=True, blank=True)
|
||||
zhuanzhi = models.CharField('枣庄市职称', max_length=50, null=True, blank=True)
|
||||
zhuanzhi_img_1 = models.CharField('职称证书图片1', max_length=100, null=True, blank=True)
|
||||
zhuanzhi_img_2 = models.CharField('职称证书图片2', max_length=100, null=True, blank=True)
|
||||
zhuanzhi_date = models.DateField('获得枣庄市职称日期', null=True, blank=True)
|
||||
zyjt_zhuanzhi = models.CharField('总院/集团职称', max_length=50, null=True, blank=True)
|
||||
zyjt_zhuanzhi_img_1 = models.CharField('总院/集团职称证书图片1', max_length=100, null=True, blank=True)
|
||||
zyjt_zhuanzhi_date = models.DateField('获得职称日期', null=True, blank=True)
|
||||
skill_rank = models.CharField('职业技能等级', max_length=50, null=True, blank=True)
|
||||
skill_rank_date = models.DateField('获得技能等级证书日期', null=True, blank=True)
|
||||
full_edu = models.CharField('全日制最高学历', max_length=50, null=True, blank=True)
|
||||
full_edu_school = models.CharField('全日制最高学历学校名称', max_length=100, null=True, blank=True)
|
||||
full_edu_major = models.CharField('全日制最高学历专业', max_length=100, null=True, blank=True)
|
||||
full_edu_time = models.CharField('全日制最高学历毕业时间', max_length=50, null=True, blank=True)
|
||||
part_edu = models.CharField('非全日制最高学历', max_length=50, null=True, blank=True)
|
||||
part_edu_school = models.CharField('非全日制最高学历学校名称', max_length=100, null=True, blank=True)
|
||||
part_edu_major = models.CharField('非全日制最高学历专业', max_length=100, null=True, blank=True)
|
||||
part_edu_time = models.CharField('非全日制最高学历毕业时间', max_length=50, null=True, blank=True)
|
||||
emergency_contact = models.CharField('紧急联系人姓名', max_length=20, null=True, blank=True)
|
||||
emergency_phone = models.CharField('紧急联系人电话', max_length=20, null=True, blank=True)
|
||||
honor = models.TextField('所获荣誉', null=True, blank=True)
|
||||
first_social_security_date = models.DateField('首次缴纳社保日期', null=True, blank=True)
|
||||
is_veteran = models.BooleanField('是否为退役军人', default=False)
|
||||
|
||||
class Meta:
|
||||
verbose_name = '员工补充信息'
|
||||
|
|
@ -290,4 +330,38 @@ class EmployeeTransfer(CommonADModel):
|
|||
content = models.TextField('个人工作内容', null=True, blank=True)
|
||||
reason = models.TextField('调动原因', null=True, blank=True)
|
||||
ticket = models.OneToOneField('wf.ticket', verbose_name='关联工单',
|
||||
on_delete=models.CASCADE, related_name='transfer_ticket', null=True, blank=True)
|
||||
on_delete=models.CASCADE, related_name='transfer_ticket', null=True, blank=True)
|
||||
|
||||
|
||||
class Probation(CommonBModel):
|
||||
"""
|
||||
TN:员工转正
|
||||
"""
|
||||
empperson = models.OneToOneField(EmpPersonInfo, verbose_name='人员信息', on_delete=models.SET_NULL, null=True, blank=True)
|
||||
ticket = models.OneToOneField('wf.ticket', verbose_name='关联工单',
|
||||
on_delete=models.CASCADE, related_name='probation_ticket', null=True, blank=True)
|
||||
reg_dept = models.ForeignKey('system.Dept', verbose_name='转正部门', on_delete=models.SET_NULL, null=True, blank=True)
|
||||
reg_post = models.ForeignKey('system.Post', verbose_name='转正岗位', on_delete=models.SET_NULL, null=True, blank=True)
|
||||
trial_date = models.DateField('试用日期')
|
||||
regular_date = models.DateField('转正日期')
|
||||
content = models.TextField('个人工作内容', null=True, blank=True)
|
||||
record_file = models.CharField('应聘人员登记表', null=True, blank=True, max_length=100)
|
||||
apply_file = models.CharField('员工转正申请表', null=True, blank=True, max_length=100)
|
||||
ppt_file = models.CharField('PPT 汇报考核', null=True, blank=True, max_length=100)
|
||||
|
||||
|
||||
class EmpContract(CommonAModel):
|
||||
"""
|
||||
TN:劳动合同
|
||||
"""
|
||||
employee = models.OneToOneField(Employee, verbose_name='人员信息', on_delete=models.SET_NULL, null=True, blank=True)
|
||||
ticket = models.OneToOneField('wf.ticket', verbose_name='关联工单',
|
||||
on_delete=models.CASCADE, related_name='contract_ticket', null=True, blank=True)
|
||||
counts = models.PositiveSmallIntegerField('合同变更次数', default=1)
|
||||
plan_renewal = models.DateField('应续签', null=True, blank=True)
|
||||
normal_renewal = models.DateField('正常续签', null=True, blank=True)
|
||||
change_date = models.IntegerField('续签/变更(年)', null=True, blank=True)
|
||||
change_reason = models.TextField('缩短续签/变更年限原因', null=True, blank=True)
|
||||
|
||||
|
||||
|
||||
|
|
@ -8,8 +8,8 @@ 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, EmpJoin, EmpPersonInfo, Leave, EmployeeTransfer)
|
||||
from apps.hrm.models import (Certificate, ClockRecord, Employee, Probation,
|
||||
NotWorkRemark, Attendance, Resignation, EmpNeed, EmpJoin, EmpPersonInfo, EmpContract, Leave, EmployeeTransfer)
|
||||
from apps.system.serializers import DeptSimpleSerializer, UserSimpleSerializer
|
||||
from django.db import transaction
|
||||
from django.core.cache import cache
|
||||
|
|
@ -349,6 +349,7 @@ class EmpNeedSerializer(CustomModelSerializer):
|
|||
model = EmpNeed
|
||||
fields = '__all__'
|
||||
|
||||
|
||||
class EmpJoinSerializer(CustomModelSerializer):
|
||||
ticket_ = TicketSimpleSerializer(source='ticket', read_only=True)
|
||||
dept_name = serializers.CharField(source='dept_need.name', read_only=True)
|
||||
|
|
@ -356,6 +357,7 @@ class EmpJoinSerializer(CustomModelSerializer):
|
|||
model = EmpJoin
|
||||
fields = '__all__'
|
||||
|
||||
|
||||
class EmpPersonInfoSerializer(CustomModelSerializer):
|
||||
class Meta:
|
||||
model = EmpPersonInfo
|
||||
|
|
@ -368,6 +370,7 @@ class EmpPersonInfoSerializer(CustomModelSerializer):
|
|||
'note',
|
||||
)
|
||||
|
||||
|
||||
class LeaveSerializer(CustomModelSerializer):
|
||||
ticket_ = TicketSimpleSerializer(source='ticket', read_only=True)
|
||||
employee_name = serializers.CharField(source='employee.name', read_only=True)
|
||||
|
|
@ -377,6 +380,7 @@ class LeaveSerializer(CustomModelSerializer):
|
|||
model = Leave
|
||||
fields = '__all__'
|
||||
|
||||
|
||||
class TransferSerializer(CustomModelSerializer):
|
||||
ticket_ = TicketSimpleSerializer(source='ticket', read_only=True)
|
||||
employee_name = serializers.CharField(source='employee.name', read_only=True)
|
||||
|
|
@ -397,4 +401,28 @@ class TransferSerializer(CustomModelSerializer):
|
|||
if validated_data['new_post'] == validated_data['original_post']:
|
||||
raise ParseError('新旧岗位相同,无需调动')
|
||||
|
||||
return super().create(validated_data)
|
||||
return super().create(validated_data)
|
||||
|
||||
|
||||
class ProbationSerializer(CustomModelSerializer):
|
||||
ticket_ = TicketSimpleSerializer(source='ticket', read_only=True)
|
||||
employee_name = serializers.CharField(source='empperson.name', read_only=True)
|
||||
post_name = serializers.CharField(source="empperson.reg_post", read_only=True)
|
||||
reg_dept_name = serializers.CharField(source='empperson.reg_dept.name', read_only=True)
|
||||
IDcard = serializers.CharField(source="empperson.IDcard", read_only=True)
|
||||
class Meta:
|
||||
model = Probation
|
||||
fields = '__all__'
|
||||
|
||||
|
||||
class EmpContractSerializer(CustomModelSerializer):
|
||||
ticket_ = TicketSimpleSerializer(source='ticket', read_only=True)
|
||||
employee_name = serializers.CharField(source='employee.name', read_only=True)
|
||||
post_name = serializers.CharField(source="employee.post.name", read_only=True)
|
||||
dept_name = serializers.CharField(source='employee.belong_dept.name', read_only=True)
|
||||
gender = serializers.CharField(source="employee.gender", read_only=True)
|
||||
join_date = serializers.CharField(source="employee.join_date", read_only=True)
|
||||
end_contract = serializers.CharField(source="employee.end_contract_date", read_only=True)
|
||||
class Meta:
|
||||
model = EmpContract
|
||||
fields = '__all__'
|
||||
|
|
@ -11,7 +11,7 @@ from django.core.cache import cache
|
|||
from rest_framework.exceptions import ParseError
|
||||
|
||||
from apps.system.models import User
|
||||
from apps.hrm.models import ClockRecord, Employee, Resignation, EmployeeTransfer
|
||||
from apps.hrm.models import ClockRecord, Employee, Resignation, EmployeeTransfer, Probation
|
||||
from apps.system.models import UserPost
|
||||
from apps.third.dahua import dhClient
|
||||
from apps.third.models import TDevice
|
||||
|
|
@ -512,10 +512,7 @@ def post_transfer(ticket: Ticket, new_ticket_data: dict, **kwargs):
|
|||
|
||||
employee = obj.employee
|
||||
user = employee.user
|
||||
|
||||
if not user:
|
||||
raise ParseError('员工未绑定系统账号,无法执行调岗')
|
||||
|
||||
|
||||
# 2️⃣ 先更新 EmployeeTransfer(避免 atomic 中用到旧数据)
|
||||
update_fields = []
|
||||
for k, v in new_ticket_data.items():
|
||||
|
|
@ -529,47 +526,52 @@ def post_transfer(ticket: Ticket, new_ticket_data: dict, **kwargs):
|
|||
|
||||
obj.save()
|
||||
|
||||
# 3️⃣ 调岗事务(岗位、部门、UserPost 一致性)
|
||||
with transaction.atomic():
|
||||
# 更新 Employee的部门和岗位
|
||||
employee.belong_dept = obj.new_dept
|
||||
employee.post = obj.new_post
|
||||
employee.save(update_fields=['belong_dept', 'post'])
|
||||
|
||||
# 3.1 校验并删除原岗位记录
|
||||
old_post_qs = UserPost.objects.filter(
|
||||
user=user,
|
||||
post=obj.original_post,
|
||||
dept=obj.original_dept
|
||||
)
|
||||
if user:
|
||||
# 3️⃣ 调岗事务(岗位、部门、UserPost 一致性)
|
||||
with transaction.atomic():
|
||||
# 3.0 校验Employee中的user是否存在
|
||||
|
||||
# 3.1 校验并删除原岗位记录
|
||||
old_post_qs = UserPost.objects.filter(
|
||||
user=user,
|
||||
post=obj.original_post,
|
||||
dept=obj.original_dept
|
||||
)
|
||||
|
||||
if not old_post_qs.exists():
|
||||
raise ParseError('原岗位记录不存在,无法调岗')
|
||||
if not old_post_qs.exists():
|
||||
raise ParseError('原岗位记录不存在,无法调岗')
|
||||
|
||||
old_post_qs.delete()
|
||||
old_post_qs.delete()
|
||||
|
||||
# 3.2 处理岗位排序(新岗位置顶)
|
||||
UserPost.objects.filter(user=user).update(sort=F('sort') + 1)
|
||||
|
||||
UserPost.objects.create(
|
||||
user=user,
|
||||
post=obj.new_post,
|
||||
dept=obj.new_dept,
|
||||
sort=1
|
||||
)
|
||||
|
||||
# 3.3 更新 Employee
|
||||
employee.belong_dept = obj.new_dept
|
||||
employee.post = obj.new_post
|
||||
employee.save(update_fields=['belong_dept', 'post'])
|
||||
|
||||
# 3.4 同步更新 User
|
||||
User.objects.filter(id=user.id).update(
|
||||
belong_dept=obj.new_dept,
|
||||
post=obj.new_post
|
||||
)
|
||||
|
||||
return obj
|
||||
# 3.2 处理岗位排序(新岗位置顶)
|
||||
UserPost.objects.filter(user=user).update(sort=F('sort') + 1)
|
||||
UserPost.objects.create(
|
||||
user=user,
|
||||
post=obj.new_post,
|
||||
dept=obj.new_dept,
|
||||
sort=1
|
||||
)
|
||||
# 3.4 同步更新 User
|
||||
User.objects.filter(id=user.id).update(
|
||||
belong_dept=obj.new_dept,
|
||||
post=obj.new_post
|
||||
)
|
||||
return obj
|
||||
|
||||
|
||||
def validate_userdept(ticket: Ticket, new_ticket_data: dict, **kwargs):
|
||||
obj = EmployeeTransfer.objects.get(id=new_ticket_data['t_id'])
|
||||
exist_dept_post = UserPost.objects.filter(user=obj.employee.user, post=obj.original_post, dept=obj.original_dept)
|
||||
if not exist_dept_post.exists():
|
||||
raise ParseError('该用户的原部门或原职务不匹配')
|
||||
raise ParseError('该用户的原部门或原职务不匹配')
|
||||
|
||||
|
||||
def probation_to_employee(ticket: Ticket, new_ticket_data: dict, **kwargs):
|
||||
obj = Probation.objects.get(id=new_ticket_data['t_id'])
|
||||
pass
|
||||
# 需要解析上传的员工信息表,存入 employee表
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
from apps.hrm.views import (CertificateViewSet, ClockRecordViewSet, EmployeeViewSet, NotWorkRemarkViewSet, EmpNeedViewSet,
|
||||
AttendanceViewSet, ResignationViewSet, EmpJoinViewSet, LeaveViewSet, TransferViewSet)
|
||||
AttendanceViewSet, ResignationViewSet, EmpJoinViewSet, EmpPersonInfoViewSet, LeaveViewSet, TransferViewSet, ProbationViewSet, EmpContractViewSet)
|
||||
from django.urls import path, include
|
||||
from rest_framework.routers import DefaultRouter
|
||||
|
||||
|
|
@ -16,8 +16,11 @@ router.register('attendance', AttendanceViewSet, basename='attendance')
|
|||
router.register('resignation', ResignationViewSet, basename='resignation')
|
||||
router.register('empneed', EmpNeedViewSet, basename='empneed')
|
||||
router.register('empjoin', EmpJoinViewSet, basename='empjoin')
|
||||
router.register('empperson', EmpPersonInfoViewSet, basename='empperson')
|
||||
router.register('leave', LeaveViewSet, basename='leave')
|
||||
router.register('transfer', TransferViewSet, basename='transfer')
|
||||
router.register('probation', ProbationViewSet, basename='probation')
|
||||
router.register('contract', EmpContractViewSet, basename='emp_contract')
|
||||
urlpatterns = [
|
||||
path(API_BASE_URL, include(router.urls)),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -12,7 +12,8 @@ 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, EmpJoin, Leave, EmployeeTransfer
|
||||
from apps.hrm.models import (Certificate, ClockRecord, Employee, NotWorkRemark, Attendance, Resignation,
|
||||
EmpNeed, EmpJoin, Leave, EmployeeTransfer, Probation, EmpPersonInfo, EmpContract)
|
||||
from apps.hrm.serializers import (CertificateCreateUpdateSerializer, CertificateSerializer, ChannelAuthoritySerializer, EmpJoinSerializer,
|
||||
ClockRecordListSerializer,
|
||||
EmployeeCreateUpdateSerializer, EmployeeDetailSerializer, EmployeeImproveSerializer,
|
||||
|
|
@ -20,24 +21,87 @@ from apps.hrm.serializers import (CertificateCreateUpdateSerializer, Certificate
|
|||
EmployeeSerializer,
|
||||
ClockRecordSimpleSerializer, ClockRecordCreateSerializer,
|
||||
NotWorkRemarkListSerializer, CorrectSerializer, AttendanceSerializer,
|
||||
ResignationSerializer, EmpNeedSerializer, LeaveSerializer, TransferSerializer)
|
||||
ResignationSerializer, EmpNeedSerializer, LeaveSerializer, TransferSerializer, ProbationSerializer, EmpContractSerializer)
|
||||
from apps.hrm.services import HrmService
|
||||
|
||||
from apps.third.dahua import dhClient
|
||||
from apps.third.tapis import dhapis
|
||||
from openpyxl import load_workbook
|
||||
from django.db import transaction
|
||||
from django.core.exceptions import ValidationError
|
||||
from datetime import datetime
|
||||
from apps.utils.export import export_excel
|
||||
from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet, EuModelViewSet
|
||||
from apps.utils.mixins import BulkCreateModelMixin, BulkDestroyModelMixin, CustomListModelMixin, RetrieveModelMixin
|
||||
from apps.wf.models import Ticket
|
||||
from apps.wf.mixins import TicketMixin
|
||||
from apps.system.models import Post, Dept
|
||||
from django.db.models import DateField
|
||||
from datetime import datetime, date
|
||||
from openpyxl.utils.datetime import from_excel
|
||||
import os
|
||||
import re
|
||||
epTypeOptions = {'employee': '正式员工', 'remployee': '相关方',
|
||||
'visitor': '访客', 'driver': '货车司机'}
|
||||
epStateOptions = {10: '在职', 20: '离职', 30: '退休'}
|
||||
crOptions = {10: '上班打卡', 20: '下班打卡', 30: ''}
|
||||
crEoptions = {10: '在岗时间短', 20: '在岗时间长', 30: '缺卡', 40: '加班'}
|
||||
|
||||
# Create your views here.
|
||||
HEAD_MAP = {
|
||||
"所属部门": "belong_dept",
|
||||
"车间": "workshop",
|
||||
"职务": "position",
|
||||
"职务聘任日期": "office_date",
|
||||
"类别": "type",
|
||||
"性别": "gender",
|
||||
"身份证号码:姓名": "name",
|
||||
"身份证号码:身份证号": "id_number",
|
||||
"银行卡号码": "bank_card",
|
||||
"入职日期": "start_date",
|
||||
"合同到期日": "contract_end_date",
|
||||
"转正日期": "regular_date",
|
||||
"出生日期": "birthday",
|
||||
"年龄": "age",
|
||||
"工龄": "work_years",
|
||||
"政治面貌": "partisan",
|
||||
"入党时间": "join_partisan_date",
|
||||
"民族": "nation",
|
||||
"婚姻状况": "marriage",
|
||||
"户口性质": "hukou_type",
|
||||
"籍贯": "birthplace",
|
||||
"户籍地址": "hukou_address",
|
||||
"现住地址": "address",
|
||||
"枣庄市职称": "zhuanzhi",
|
||||
"枣庄市职称证书:图片_1": "zhuanzhi_img_1",
|
||||
"枣庄市职称证书:图片_2": "zhuanzhi_img_2",
|
||||
"获得枣庄市职称日期": "zhuanzhi_date",
|
||||
"总院/集团职称": "zyjt_zhuanzhi",
|
||||
"总院/集团职称证书:图片_1": "zyjt_zhuanzhi_img_1",
|
||||
"获得职称日期": "zyjt_zhuanzhi_date",
|
||||
"职业技能等级": "skill_rank",
|
||||
"获得技能等级证书日期": "skill_rank_date",
|
||||
"全日制最高学历": "full_edu",
|
||||
"全日制最高学历学校名称": "full_edu_school",
|
||||
"全日制最高学历所学专业": "full_edu_major",
|
||||
"全日制最高学历入学时间-毕业时间": "full_edu_time",
|
||||
"非全日制最高学历": "part_edu",
|
||||
"非全日制最高学历学校名称": "part_edu_school",
|
||||
"非全日制最高学历所学专业": "part_edu_major",
|
||||
"非全日制最高学历入学时间-毕业时间": "part_edu_time",
|
||||
"本人联系电话": "phone",
|
||||
"紧急联系人姓名": "emergency_contact",
|
||||
"紧急联系人电话": "emergency_phone",
|
||||
"所获荣誉": "honor",
|
||||
"首次缴纳社保日期": "first_social_security_date",
|
||||
"是否为退役军人": "is_veteran",
|
||||
"个人邮箱":"email",
|
||||
}
|
||||
|
||||
def cell_value(cell):
|
||||
val = cell.value
|
||||
if isinstance(val, datetime):
|
||||
return val.date()
|
||||
return val.strip() if isinstance(val, str) else val
|
||||
|
||||
class EmployeeViewSet(CustomModelViewSet):
|
||||
"""
|
||||
|
|
@ -244,6 +308,203 @@ class EmployeeViewSet(CustomModelViewSet):
|
|||
)
|
||||
return Response({'path': export_excel(field_data, data, '人员信息')})
|
||||
|
||||
@action(methods=['post'], detail=False, perms_map={'post': 'employee.import_excel'},
|
||||
serializer_class=serializers.Serializer)
|
||||
def import_excel(self, request, pk=None):
|
||||
import logging
|
||||
myLogger = logging.getLogger('log')
|
||||
"""导入excel"""
|
||||
file_path = request.data.get('file_path')
|
||||
relative_path = file_path.lstrip('/')
|
||||
abs_path = os.path.join(settings.BASE_DIR, relative_path)
|
||||
wb = load_workbook(abs_path, data_only=True)
|
||||
sheet = wb.active
|
||||
|
||||
rows = list(sheet.iter_rows())
|
||||
if len(rows) < 2:
|
||||
raise ParseError('Excel 没有数据')
|
||||
|
||||
# 获取表头
|
||||
headers = [cell_value(c) for c in rows[0]]
|
||||
field_index = {}
|
||||
for idx, header in enumerate(headers):
|
||||
if header in HEAD_MAP:
|
||||
field_index[HEAD_MAP[header]] = idx
|
||||
|
||||
# 外键缓存
|
||||
dept_map = {d.name: d.id for d in Dept.objects.all()}
|
||||
TYPE_MAPPING = {
|
||||
'光芯': 'employee',
|
||||
'外包': 'remployee'
|
||||
}
|
||||
success = 0
|
||||
errors = []
|
||||
|
||||
# 获取模型字段
|
||||
model_fields = {f.name: f for f in Employee._meta.fields}
|
||||
|
||||
for row_num, row in enumerate(rows[1:], start=2):
|
||||
try:
|
||||
data = {}
|
||||
for field, idx in field_index.items():
|
||||
raw_value = cell_value(row[idx])
|
||||
model_field = model_fields.get(field)
|
||||
value = convert_field_value(model_field, raw_value, row_num)
|
||||
data[field] = value
|
||||
|
||||
# 移除可能的id字段
|
||||
if 'id' in data:
|
||||
del data['id']
|
||||
|
||||
# 获取唯一标识
|
||||
id_number = data.get("id_number")
|
||||
name = data.get("name")
|
||||
|
||||
if not id_number or not name:
|
||||
raise ParseError(f'第{row_num}行,身份证号或姓名为空')
|
||||
|
||||
myLogger.info(f"处理第{row_num}行:{name} - {id_number}")
|
||||
# 处理人员类型
|
||||
if 'type' in data and data['type']:
|
||||
excel_type = data['type']
|
||||
if excel_type in TYPE_MAPPING:
|
||||
data['type'] = TYPE_MAPPING[excel_type]
|
||||
else:
|
||||
raise ParseError(f'第{row_num}行,人员类型"{excel_type}"无效,有效类型:{", ".join(TYPE_MAPPING.keys())}')
|
||||
# 处理部门外键
|
||||
if 'belong_dept' in data and data['belong_dept']:
|
||||
dept_name = data.pop('belong_dept')
|
||||
if dept_name not in dept_map:
|
||||
raise ParseError(f'第{row_num}行,部门"{dept_name}"不存在')
|
||||
data['belong_dept_id'] = dept_map[dept_name]
|
||||
|
||||
# 数据验证
|
||||
if data.get('phone'):
|
||||
if not re.match(r'^1[3-9]\d{9}$', data['phone']):
|
||||
raise ParseError(f'第{row_num}行,手机号格式不正确')
|
||||
|
||||
if data.get('id_number'):
|
||||
if not re.match(r'^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$', data['id_number']):
|
||||
raise ParseError(f'第{row_num}行,身份证号格式不正确')
|
||||
|
||||
# 查找或更新
|
||||
try:
|
||||
with transaction.atomic():
|
||||
obj, created = Employee.objects.update_or_create(
|
||||
id_number=id_number,
|
||||
name=name,
|
||||
defaults=data
|
||||
)
|
||||
except Exception as e:
|
||||
raise
|
||||
if created:
|
||||
myLogger.info(f"✅ 第{row_num}行新增成功:{name}")
|
||||
else:
|
||||
myLogger.info(f"✅ 第{row_num}行更新成功:{name}")
|
||||
|
||||
success += 1
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f'第{row_num}行处理失败:{str(e)}'
|
||||
myLogger.error(f"❌ {error_msg}")
|
||||
errors.append({'row': row_num, 'error': str(e)})
|
||||
|
||||
myLogger.info(f"\n处理完成:成功{success}条,失败{len(errors)}条")
|
||||
|
||||
return Response({'success': success, 'errors': errors})
|
||||
|
||||
|
||||
def convert_field_value(model_field, value, row_num):
|
||||
"""
|
||||
根据模型字段类型自动转换 Excel 值
|
||||
"""
|
||||
from django.db.models import DateField, BooleanField, IntegerField, FloatField, CharField
|
||||
if value in [None, ""]:
|
||||
return None
|
||||
|
||||
# ===== 日期字段 =====
|
||||
if isinstance(model_field, CharField):
|
||||
if isinstance(value, str):
|
||||
if 'e' in value.lower():
|
||||
try:
|
||||
return str(int(float(value)))
|
||||
except ValueError:
|
||||
pass
|
||||
if '.' in value:
|
||||
try:
|
||||
return str(int(float(value)))
|
||||
except ValueError:
|
||||
pass
|
||||
return str(value)
|
||||
|
||||
if isinstance(model_field, DateField):
|
||||
|
||||
# 已经是 date
|
||||
if isinstance(value, date):
|
||||
return value
|
||||
|
||||
# datetime
|
||||
if isinstance(value, datetime):
|
||||
return value.date()
|
||||
|
||||
# Excel 数字日期
|
||||
if isinstance(value, (int, float)):
|
||||
try:
|
||||
return from_excel(value).date()
|
||||
except Exception:
|
||||
raise ParseError(f'第{row_num}行,字段 {model_field.name} 日期格式错误')
|
||||
|
||||
# 字符串日期
|
||||
if isinstance(value, str):
|
||||
value = value.strip()
|
||||
formats = [
|
||||
"%Y-%m-%d",
|
||||
"%Y/%m/%d",
|
||||
"%Y.%m.%d",
|
||||
"%Y%m%d",
|
||||
"%Y年%m月%d日",
|
||||
]
|
||||
for fmt in formats:
|
||||
try:
|
||||
return datetime.strptime(value, fmt).date()
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
raise ParseError(f'第{row_num}行,字段 {model_field.name} 日期格式不正确')
|
||||
|
||||
raise ParseError(f'第{row_num}行,字段 {model_field.name} 日期类型错误')
|
||||
|
||||
# ===== 布尔字段 =====
|
||||
if isinstance(model_field, BooleanField):
|
||||
|
||||
if value in [True, 1, '1']:
|
||||
return True
|
||||
if value in [False, 0, '0']:
|
||||
return False
|
||||
|
||||
if isinstance(value, str):
|
||||
value = value.strip()
|
||||
if value in ['是', 'Y', 'y', 'yes', 'YES', 'True', 'true']:
|
||||
return True
|
||||
if value in ['否', 'N', 'n', 'no', 'NO', 'False', 'false']:
|
||||
return False
|
||||
|
||||
raise ParseError(f'第{row_num}行,字段 {model_field.name} 只能填写 是 或 否')
|
||||
|
||||
# ===== 整数字段 =====
|
||||
if isinstance(model_field, IntegerField):
|
||||
try:
|
||||
return int(value)
|
||||
except:
|
||||
raise ParseError(f'第{row_num}行,字段 {model_field.name} 必须为整数')
|
||||
|
||||
# ===== 浮点字段 =====
|
||||
if isinstance(model_field, FloatField):
|
||||
try:
|
||||
return float(value)
|
||||
except:
|
||||
raise ParseError(f'第{row_num}行,字段 {model_field.name} 必须为数字')
|
||||
return value
|
||||
|
||||
class AttendanceViewSet(CustomModelViewSet):
|
||||
"""
|
||||
|
|
@ -395,6 +656,9 @@ class CertificateViewSet(CustomModelViewSet):
|
|||
|
||||
|
||||
class ResignationViewSet(TicketMixin, EuModelViewSet):
|
||||
"""
|
||||
离职记录
|
||||
"""
|
||||
select_related_fields = ['employee', 'employee__belong_dept', 'employee__post']
|
||||
queryset = Resignation.objects.all()
|
||||
serializer_class = ResignationSerializer
|
||||
|
|
@ -426,6 +690,9 @@ class ResignationViewSet(TicketMixin, EuModelViewSet):
|
|||
|
||||
|
||||
class EmpNeedViewSet(TicketMixin, EuModelViewSet):
|
||||
"""
|
||||
员工需求
|
||||
"""
|
||||
queryset = EmpNeed.objects.all()
|
||||
serializer_class = EmpNeedSerializer
|
||||
filterset_fields = ['dept_need']
|
||||
|
|
@ -437,6 +704,9 @@ class EmpNeedViewSet(TicketMixin, EuModelViewSet):
|
|||
|
||||
|
||||
class EmpJoinViewSet(TicketMixin, EuModelViewSet):
|
||||
"""
|
||||
员工入职
|
||||
"""
|
||||
queryset = EmpJoin.objects.all()
|
||||
serializer_class = EmpJoinSerializer
|
||||
workflow_key = "wf_empjoin"
|
||||
|
|
@ -456,6 +726,9 @@ class EmpJoinViewSet(TicketMixin, EuModelViewSet):
|
|||
|
||||
|
||||
class LeaveViewSet(TicketMixin, EuModelViewSet):
|
||||
"""
|
||||
员工请假
|
||||
"""
|
||||
select_related_fields = [
|
||||
'employee',
|
||||
'employee__belong_dept',
|
||||
|
|
@ -469,8 +742,22 @@ class LeaveViewSet(TicketMixin, EuModelViewSet):
|
|||
|
||||
def gen_other_ticket_data(self, instance):
|
||||
return {"hour": instance.hour if instance.hour else None}
|
||||
|
||||
|
||||
|
||||
class EmpPersonInfoViewSet(CustomModelViewSet, EuModelViewSet):
|
||||
"""
|
||||
入职人员信息
|
||||
"""
|
||||
queryset = EmpPersonInfo.objects.all()
|
||||
serializer_class = EmpPersonInfoSerializer
|
||||
filterset_fields = ['post']
|
||||
search_fields = ['name', 'post']
|
||||
|
||||
|
||||
class TransferViewSet(TicketMixin, EuModelViewSet):
|
||||
"""
|
||||
员工岗位调动
|
||||
"""
|
||||
select_related_fields = [
|
||||
'employee',
|
||||
'employee__belong_dept',
|
||||
|
|
@ -484,4 +771,38 @@ class TransferViewSet(TicketMixin, EuModelViewSet):
|
|||
workflow_key = "wf_transfer"
|
||||
|
||||
def gen_other_ticket_data(self, instance):
|
||||
return {"name": instance.employee.name if instance.employee.name else None}
|
||||
return {"name": instance.employee.name if instance.employee.name else None}
|
||||
|
||||
class ProbationViewSet(TicketMixin, EuModelViewSet):
|
||||
"""
|
||||
员工转正
|
||||
"""
|
||||
select_related_fields = [
|
||||
'empperson',
|
||||
'ticket',
|
||||
'reg_dept',
|
||||
'reg_post'
|
||||
]
|
||||
queryset = Probation.objects.all()
|
||||
serializer_class = ProbationSerializer
|
||||
filterset_fields = ['empperson__name', 'reg_dept']
|
||||
search_fields = ["empperson__name", "reg_post__name", "reg_dept__name"]
|
||||
workflow_key = "wf_probation"
|
||||
|
||||
def gen_other_ticket_data(self, instance):
|
||||
return {"name": instance.empperson.name if instance.empperson.name else None}
|
||||
|
||||
|
||||
class EmpContractViewSet(TicketMixin, EuModelViewSet):
|
||||
"""
|
||||
员工合同
|
||||
"""
|
||||
queryset = EmpContract.objects.all()
|
||||
serializer_class = EmpContractSerializer
|
||||
filterset_fields = ['employee', 'employee__belong_dept','employee__name', 'employee__post']
|
||||
search_fields = ["employee__name", "employee__post", "employee__belong_dept"]
|
||||
workflow_key = "wf_contract"
|
||||
|
||||
def gen_other_ticket_data(self, instance):
|
||||
return {"name": instance.employee.name if instance.employee.name else None}
|
||||
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 6.9 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 57 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 154 KiB |
Loading…
Reference in New Issue