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