factory/apps/hrm/models.py

367 lines
18 KiB
Python
Executable File

from django.db import models
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:员工信息
"""
JOB_ON = 10
JOB_OFF = 20
JOB_RETIRE = 30
jobstate_choices = (
(JOB_ON, '在职'),
(JOB_OFF, '离职'),
(JOB_RETIRE, '退休')
)
PEOPLE_TYPE_CHOICES = (
('employee', '内部员工'),
('remployee', '相关方人员'),
('visitor', '访客'),
('driver', '司机')
)
user = models.OneToOneField(User,
verbose_name='系统账号',
related_name='employee',
on_delete=models.SET_NULL, null=True, blank=True)
name = models.CharField('姓名', max_length=20)
phone = models.CharField('手机号', max_length=11, null=True, blank=True)
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)
signature = models.CharField('签名图片', max_length=200, null=True, blank=True)
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) # 存储的是字典(模型名:人脸数据)
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 = '员工补充信息'
verbose_name_plural = verbose_name
def __str__(self):
return self.name
def save(self, *args, **kwargs) -> None:
return super().save(*args, **kwargs)
# class Card(CommonAModel):
# """
# 卡
# """
# CARD_FACE = 10
# CARD_LOCATION = 20
class NotWorkRemark(CommonADModel):
"""
TN:离岗说明
"""
not_work_date = models.DateField('未打卡日期')
user = models.ForeignKey(User, verbose_name='用户', on_delete=models.CASCADE)
remark = models.CharField('未打卡说明', null=True, blank=True, max_length=200)
class Attendance(CommonADModel):
"""
TN:到岗记录
"""
ATT_STATE_CHOICES = [
('pending', '待定'),
('normal', '正常'),
('late', '迟到'),
('early_leave', '早退'),
('absent', '未到岗'),
('leave', '请假'),
# 可以根据需要添加更多状态
]
user = models.ForeignKey(
User, verbose_name='关联人员', on_delete=models.CASCADE)
work_date = models.DateField('工作日期')
shift = models.ForeignKey(
'mtm.shift', verbose_name='班次', on_delete=models.CASCADE)
team = models.ForeignKey('mtm.team', verbose_name='班组',
on_delete=models.SET_NULL, null=True, blank=True)
post = models.ForeignKey('system.post', verbose_name='岗位',
on_delete=models.SET_NULL, null=True, blank=True)
work_time_start = models.DateTimeField('工作开始时间')
work_time_end = models.DateTimeField('工作结束时间')
state = models.CharField('状态', max_length=20,
choices=ATT_STATE_CHOICES, default='pending', help_text=str(ATT_STATE_CHOICES))
note = models.TextField('备注信息', default='', blank=True)
# class Meta:
# unique_together = ('user', 'work_date', 'shift')
class ClockRecord(BaseModel):
"""
TN:打卡记录
"""
ClOCK_ON = 10
CLOCK_OFF = 20
CLOCK_ING = 30
type_choice = (
(ClOCK_ON, '上班打卡'),
(CLOCK_OFF, '下班打卡'),
(CLOCK_ING, '忽略'), # 尽量不用
)
E_TYPE_LESS = 10
E_TYPE_MORE = 20
E_TYPE_MISS = 30
E_TYPE_ADD = 40
E_TYPE_CHOISE = (
(E_TYPE_LESS, '在岗时间短'),
(E_TYPE_MORE, '在岗时间长'),
(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)
clock_time = models.DateTimeField('打卡时间', 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)
note = models.CharField('备注', max_length=20, default='')
attendance = models.ForeignKey(
Attendance, on_delete=models.SET_NULL, verbose_name='关联到岗记录', null=True, blank=True)
class Certificate(CommonAModel):
"""
TN:证书
"""
CERT_OK = 10
CERT_EXPIRING = 20
CERT_EXPIRED = 30
CERTIFICATE_TYPE_CHOICES = (
(10, '特种作业证书'),
(20, '特种设备操作证书'),
(30, '安全管理人员证书')
)
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)
issue_date = models.DateField('发证日期')
expiration_date = models.DateField('有效期')
state = models.PositiveSmallIntegerField('证书状态', default=CERT_OK, choices=((CERT_OK, '有效'), (CERT_EXPIRING, '即将过期'), (CERT_EXPIRED, '已过期')))
review_date = models.DateField('下一次复审日期')
file = models.TextField('文件地址', null=True, blank=True)
def get_state(self, need_update=False):
now = timezone.now().date()
new_state = self.CERT_EXPIRED
if self.expiration_date - timedelta(days=30) <= now < self.expiration_date:
new_state = self.CERT_EXPIRING
elif now < self.expiration_date - timedelta(days=30):
new_state = self.CERT_OK
if need_update and new_state != self.state:
self.state = new_state
self.save(update_fields=['state'])
return new_state
class Resignation(CommonADModel):
"""
TN:离职记录
"""
employee = models.ForeignKey(Employee, verbose_name='员工', on_delete=models.CASCADE)
end_date = models.DateField('离职日期')
reason = models.TextField('离职原因')
handle_date = models.DateField('办理离职交接日期', null=True, blank=True)
ticket = models.ForeignKey('wf.ticket', verbose_name='关联工单', on_delete=models.SET_NULL, null=True, blank=True, related_name='resignation_ticket')
class EmpNeed(CommonADModel):
"""
TN:员工需求
"""
dept_need = models.ForeignKey('system.Dept', verbose_name='需求部门', on_delete=models.CASCADE)
post_need = models.TextField("需求岗位")
count_need = models.PositiveIntegerField('需求人数')
salary = models.PositiveIntegerField("工资报酬")
arrival_date = models.DateField('到岗日期')
reason = models.TextField('申请理由', help_text="新增人员/该岗原人员离职或辞职或辞退需补充/其他原因")
duty = models.TextField("岗位人员职责描述")
gender = models.CharField('性别要求', max_length=10, help_text="男/女/不限")
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)
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)
post = models.CharField('岗位', max_length=20, null=True, blank=True)
note = models.TextField('备注', null=True, blank=True)
class Leave(CommonADModel):
"""
TN:员工请假
"""
E_TYPE_CHOISE = (
(10, '事假'),
(20, '病假'),
(30, '婚假'),
(40, '丧假'),
(50, '公假'),
(60, '工伤'),
(70, '产假'),
(80, '护理假'),
(90, '其他'),
)
employee = models.ForeignKey(Employee, verbose_name='员工', on_delete=models.CASCADE)
start_date = models.DateTimeField('开始日期')
end_date = models.DateTimeField('结束日期')
leave_type = models.PositiveSmallIntegerField('请假类型', choices=E_TYPE_CHOISE, default=10)
reason = models.TextField('请假事由')
hour = models.PositiveSmallIntegerField('请假时长', null=True, blank=True)
file = models.TextField('证明', null=True, blank=True)
ticket = models.OneToOneField('wf.ticket', verbose_name='关联工单',
on_delete=models.CASCADE, related_name='leave_ticket', null=True, blank=True)
class EmployeeTransfer(CommonADModel):
"""
TN:员工岗位调动
"""
employee = models.ForeignKey(Employee, verbose_name='员工', on_delete=models.CASCADE)
is_change = models.BooleanField('是否跨部门调动', default=False)
is_promotion = models.BooleanField('是否晋升', default=False)
new_dept = models.ForeignKey('system.Dept', verbose_name='新部门', related_name="transfer_new_dept", on_delete=models.CASCADE, null=True, blank=True)
new_post = models.ForeignKey('system.Post', verbose_name='新岗位', related_name="transfer_new_post", on_delete=models.CASCADE, null=True, blank=True)
original_dept = models.ForeignKey('system.Dept', verbose_name='原部门', related_name="transfer_original_dept", on_delete=models.CASCADE, null=True, blank=True)
original_post = models.ForeignKey('system.Post', verbose_name='原岗位', related_name="transfer_original_post", on_delete=models.CASCADE, null=True, blank=True)
transfer_date = models.DateField('调岗日期')
original_slary = models.DecimalField('原岗位薪资', max_digits=10, decimal_places=2, null=True, blank=True)
new_slary = models.DecimalField('调岗后薪资', max_digits=10, decimal_places=2, null=True, blank=True)
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)
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=0)
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)