192 lines
7.3 KiB
Python
Executable File
192 lines
7.3 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
|
|
|
|
|
|
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', '司机')
|
|
)
|
|
type = models.CharField('人员类型', default='employee',
|
|
max_length=10, choices=PEOPLE_TYPE_CHOICES)
|
|
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)
|
|
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(
|
|
'在职状态', 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) # 存储的是字典(模型名:人脸数据)
|
|
|
|
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 |