同步一次数据库
This commit is contained in:
parent
a5f19641b6
commit
c192467d58
|
@ -13,5 +13,6 @@ celerybeat-schedule.dat
|
||||||
celerybeat-schedule.dir
|
celerybeat-schedule.dir
|
||||||
db.sqlite3
|
db.sqlite3
|
||||||
server/conf.py
|
server/conf.py
|
||||||
|
server/conf.json
|
||||||
sh/*
|
sh/*
|
||||||
nohup.out
|
nohup.out
|
|
@ -0,0 +1,76 @@
|
||||||
|
# Generated by Django 3.2.12 on 2022-06-17 07:19
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import django.utils.timezone
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
('system', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Access',
|
||||||
|
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='删除标记')),
|
||||||
|
('type', models.PositiveSmallIntegerField(choices=[(10, '准入'), (20, '禁入')], verbose_name='准入类型')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Area',
|
||||||
|
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='名称')),
|
||||||
|
('type', models.PositiveSmallIntegerField(choices=[(10, '固定'), (20, '临时')], default=10, verbose_name='区域类型')),
|
||||||
|
('level', models.PositiveSmallIntegerField(verbose_name='区域等级')),
|
||||||
|
('number', models.CharField(blank=True, max_length=20, null=True, verbose_name='编号')),
|
||||||
|
('visitor_yes', models.BooleanField(default=False, verbose_name='准许访客人员')),
|
||||||
|
('remployee_yes', models.BooleanField(default=False, verbose_name='准许相关方人员')),
|
||||||
|
('employee_yes', models.BooleanField(default=True, verbose_name='准许全部员工')),
|
||||||
|
('third_info', models.JSONField(blank=True, default=dict, verbose_name='三方信息')),
|
||||||
|
('belong_dept', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='area_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='area_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
|
||||||
|
('posts_access', models.ManyToManyField(through='am.Access', to='system.Post')),
|
||||||
|
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='area_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='access',
|
||||||
|
name='area',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='am.area', verbose_name='关联区域'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='access',
|
||||||
|
name='create_by',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='access_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='access',
|
||||||
|
name='post',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='system.post', verbose_name='关联岗位'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='access',
|
||||||
|
name='update_by',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='access_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -12,15 +12,22 @@ class Area(CommonBModel):
|
||||||
AREA_LEVEL_2 = 20
|
AREA_LEVEL_2 = 20
|
||||||
AREA_LEVEL_3 = 30
|
AREA_LEVEL_3 = 30
|
||||||
AREA_LEVEL_4 = 40
|
AREA_LEVEL_4 = 40
|
||||||
AREA_LEVEL_HOICES = (
|
AREA_LEVEL_CHOICES = (
|
||||||
(AREA_LEVEL_1, '办公'),
|
(AREA_LEVEL_1, '办公'),
|
||||||
(AREA_LEVEL_2, '生产一般'),
|
(AREA_LEVEL_2, '生产一般'),
|
||||||
(AREA_LEVEL_3, '生产重点'),
|
(AREA_LEVEL_3, '生产重点'),
|
||||||
(AREA_LEVEL_4, '四级')
|
(AREA_LEVEL_4, '四级')
|
||||||
)
|
)
|
||||||
|
AREA_TYPE_FIX = 10
|
||||||
|
AREA_TYPE_TEMP = 20
|
||||||
|
AREA_TYPE_CHOICES = (
|
||||||
|
(10, '固定'),
|
||||||
|
(20, '临时')
|
||||||
|
)
|
||||||
name = models.CharField('名称', max_length=20)
|
name = models.CharField('名称', max_length=20)
|
||||||
|
type = models.PositiveSmallIntegerField('区域类型', default=10, choices=AREA_TYPE_CHOICES)
|
||||||
level = models.PositiveSmallIntegerField('区域等级')
|
level = models.PositiveSmallIntegerField('区域等级')
|
||||||
sort_str = models.CharField('排序字符', max_length=12, default='1')
|
number = models.CharField('编号', max_length=20, null=True, blank=True)
|
||||||
visitor_yes = models.BooleanField('准许访客人员', default=False)
|
visitor_yes = models.BooleanField('准许访客人员', default=False)
|
||||||
remployee_yes = models.BooleanField('准许相关方人员', default=False)
|
remployee_yes = models.BooleanField('准许相关方人员', default=False)
|
||||||
employee_yes = models.BooleanField('准许全部员工', default=True)
|
employee_yes = models.BooleanField('准许全部员工', default=True)
|
||||||
|
|
|
@ -11,7 +11,7 @@ class AreaSimpleSerializer(CustomModelSerializer):
|
||||||
class AreaCreateUpdateSerializer(CustomModelSerializer):
|
class AreaCreateUpdateSerializer(CustomModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Area
|
model = Area
|
||||||
fields = ['name', 'level', 'sort_srt', 'visitor_yes', 'remployee_yes', 'employee_yes', 'belong_dept']
|
fields = ['name', 'level', 'number', 'visitor_yes', 'remployee_yes', 'employee_yes', 'belong_dept']
|
||||||
|
|
||||||
|
|
||||||
class AccessCreateSerializer(CustomModelSerializer):
|
class AccessCreateSerializer(CustomModelSerializer):
|
||||||
|
|
|
@ -0,0 +1,129 @@
|
||||||
|
# Generated by Django 3.2.12 on 2022-06-17 07:19
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import django.utils.timezone
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('am', '0001_initial'),
|
||||||
|
('third', '0001_initial'),
|
||||||
|
('hrm', '0002_auto_20220617_1124'),
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
('system', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Event',
|
||||||
|
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='删除标记')),
|
||||||
|
('imgs', models.JSONField(blank=True, default=list, verbose_name='事件图片')),
|
||||||
|
('location', models.JSONField(blank=True, default=dict, verbose_name='事件点位坐标')),
|
||||||
|
('peope_type', models.CharField(blank=True, choices=[('employee', '内部员工'), ('remployee', '相关方人员'), ('visitor', '访客')], max_length=20, null=True, verbose_name='当事人员类型')),
|
||||||
|
('handle_time', models.DateTimeField(blank=True, null=True, verbose_name='处理时间')),
|
||||||
|
('handle_desc', models.TextField(blank=True, null=True, verbose_name='处理描述')),
|
||||||
|
('is_pushed', models.BooleanField(default=False, verbose_name='是否已推送')),
|
||||||
|
('area', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='am.area', verbose_name='发生区域')),
|
||||||
|
('belong_dept', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='event_belong_dept', to='system.dept', verbose_name='所属部门')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='EventCate',
|
||||||
|
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='删除标记')),
|
||||||
|
('code', models.CharField(max_length=10, unique=True, verbose_name='标识')),
|
||||||
|
('name', models.CharField(max_length=20, unique=True, verbose_name='名称')),
|
||||||
|
('trigger', models.PositiveSmallIntegerField(choices=[(10, '监控'), (20, '定位')], default=10, verbose_name='触发方式')),
|
||||||
|
('speaker_on', models.BooleanField(default=True, verbose_name='开启音响报警')),
|
||||||
|
('filter_area_level', models.PositiveSmallIntegerField(choices=[(10, '办公'), (20, '生产一般'), (30, '生产重点'), (40, '四级')], default=10, verbose_name='固定音响区域级别过滤')),
|
||||||
|
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='eventcate_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
|
||||||
|
('speakers', models.ManyToManyField(to='third.TDevice', verbose_name='固定音响')),
|
||||||
|
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='eventcate_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='NotifySetting',
|
||||||
|
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='删除标记')),
|
||||||
|
('filter_sender', models.PositiveSmallIntegerField(blank=True, null=True, verbose_name='提醒人员过滤')),
|
||||||
|
('filter_area_level', models.PositiveSmallIntegerField(blank=True, null=True, verbose_name='区域级别过滤')),
|
||||||
|
('sms_enable', models.BooleanField(default=False, verbose_name='短信通知')),
|
||||||
|
('wechat_enable', models.BooleanField(default=False, verbose_name='开启微信通知')),
|
||||||
|
('can_handle', models.BooleanField(default=False, verbose_name='是否可处理')),
|
||||||
|
('sort', models.PositiveIntegerField(default=1, verbose_name='排序')),
|
||||||
|
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='notifysetting_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
|
||||||
|
('event_cate', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='ecm.eventcate', to_field='code', verbose_name='关联事件种类')),
|
||||||
|
('post', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='system.post', verbose_name='提醒岗位')),
|
||||||
|
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='notifysetting_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Remind',
|
||||||
|
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='删除标记')),
|
||||||
|
('msg', models.TextField(blank=True, null=True, verbose_name='推送文本')),
|
||||||
|
('is_read', models.BooleanField(default=False, verbose_name='站内信已读')),
|
||||||
|
('can_handle', models.BooleanField(default=False, verbose_name='是否可处理')),
|
||||||
|
('dept', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='system.dept', verbose_name='部门')),
|
||||||
|
('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='ecm.event', verbose_name='关联事件')),
|
||||||
|
('notify_setting', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='ecm.notifysetting', verbose_name='通过哪个配置')),
|
||||||
|
('post', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='system.post', verbose_name='岗位')),
|
||||||
|
('recipient', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='接收人')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='event',
|
||||||
|
name='cate',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='ecm.eventcate', verbose_name='事件种类'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='event',
|
||||||
|
name='create_by',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='event_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='event',
|
||||||
|
name='handle_user',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='处理人'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='event',
|
||||||
|
name='people',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='hrm.employee', verbose_name='当事人'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='event',
|
||||||
|
name='update_by',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='event_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -3,6 +3,7 @@ from apps.am.models import Area
|
||||||
from apps.hrm.models import Employee
|
from apps.hrm.models import Employee
|
||||||
from apps.utils.models import BaseModel, CommonAModel, CommonBModel
|
from apps.utils.models import BaseModel, CommonAModel, CommonBModel
|
||||||
from apps.system.models import Dept, Post, User
|
from apps.system.models import Dept, Post, User
|
||||||
|
from apps.third.models import TDevice
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
# Create your models here.
|
# Create your models here.
|
||||||
|
|
||||||
|
@ -19,14 +20,14 @@ class EventCate(CommonAModel):
|
||||||
name = models.CharField('名称', max_length=20, unique=True)
|
name = models.CharField('名称', max_length=20, unique=True)
|
||||||
trigger = models.PositiveSmallIntegerField('触发方式', default=10, choices=EVENT_TRIGGER_CHOICES)
|
trigger = models.PositiveSmallIntegerField('触发方式', default=10, choices=EVENT_TRIGGER_CHOICES)
|
||||||
speaker_on = models.BooleanField('开启音响报警', default=True)
|
speaker_on = models.BooleanField('开启音响报警', default=True)
|
||||||
speakers = models.ManyToManyField('third.tdevice', verbose_name='固定音响')
|
speakers = models.ManyToManyField(TDevice, verbose_name='固定音响')
|
||||||
filter_area_level = models.PositiveSmallIntegerField('固定音响区域级别过滤', choices=Area.AREA_LEVEL_HOICES,
|
filter_area_level = models.PositiveSmallIntegerField('固定音响区域级别过滤', choices=Area.AREA_LEVEL_CHOICES,
|
||||||
default=Area.AREA_LEVEL_1)
|
default=Area.AREA_LEVEL_1)
|
||||||
|
|
||||||
|
|
||||||
class PushSetting(CommonAModel):
|
class NotifySetting(CommonAModel):
|
||||||
"""
|
"""
|
||||||
推送配置
|
提醒配置
|
||||||
"""
|
"""
|
||||||
PUSH_FILTER1_CHOICES = (
|
PUSH_FILTER1_CHOICES = (
|
||||||
(10, '当事人部门'),
|
(10, '当事人部门'),
|
||||||
|
@ -36,11 +37,12 @@ class PushSetting(CommonAModel):
|
||||||
)
|
)
|
||||||
event_cate = models.ForeignKey(EventCate, verbose_name='关联事件种类',
|
event_cate = models.ForeignKey(EventCate, verbose_name='关联事件种类',
|
||||||
to_field='code', on_delete=models.CASCADE)
|
to_field='code', on_delete=models.CASCADE)
|
||||||
post = models.ForeignKey(Post, verbose_name='推送岗位',
|
post = models.ForeignKey(Post, verbose_name='提醒岗位',
|
||||||
on_delete=models.CASCADE, null=True, blank=True)
|
on_delete=models.CASCADE, null=True, blank=True)
|
||||||
filter_to_user = models.PositiveSmallIntegerField('推送人员过滤', null=True, blank=True)
|
filter_sender = models.PositiveSmallIntegerField('提醒人员过滤', null=True, blank=True)
|
||||||
filter_area_level = models.PositiveSmallIntegerField('区域级别过滤', null=True, blank=True)
|
filter_area_level = models.PositiveSmallIntegerField('区域级别过滤', null=True, blank=True)
|
||||||
sms_enable = models.BooleanField('短信通知', default=False)
|
sms_enable = models.BooleanField('短信通知', default=False)
|
||||||
|
wechat_enable = models.BooleanField('开启微信通知', default=False)
|
||||||
can_handle = models.BooleanField('是否可处理', default=False)
|
can_handle = models.BooleanField('是否可处理', default=False)
|
||||||
sort = models.PositiveIntegerField('排序', default=1)
|
sort = models.PositiveIntegerField('排序', default=1)
|
||||||
|
|
||||||
|
@ -49,7 +51,7 @@ class Event(CommonBModel):
|
||||||
"""
|
"""
|
||||||
事件
|
事件
|
||||||
"""
|
"""
|
||||||
cate = models.ForeignKey(EventCate, verbose_name='事件种类')
|
cate = models.ForeignKey(EventCate, verbose_name='事件种类', on_delete=models.CASCADE)
|
||||||
imgs = models.JSONField('事件图片', default=list, null=False, blank=True)
|
imgs = models.JSONField('事件图片', default=list, null=False, blank=True)
|
||||||
area = models.ForeignKey(Area, verbose_name='发生区域', on_delete=models.CASCADE)
|
area = models.ForeignKey(Area, verbose_name='发生区域', on_delete=models.CASCADE)
|
||||||
location = models.JSONField('事件点位坐标', default=dict, null=False, blank=True)
|
location = models.JSONField('事件点位坐标', default=dict, null=False, blank=True)
|
||||||
|
@ -64,22 +66,20 @@ class Event(CommonBModel):
|
||||||
is_pushed = models.BooleanField('是否已推送', default=False)
|
is_pushed = models.BooleanField('是否已推送', default=False)
|
||||||
|
|
||||||
|
|
||||||
class Push(BaseModel):
|
class Remind(BaseModel):
|
||||||
"""
|
"""
|
||||||
推送情况
|
事件提醒表
|
||||||
"""
|
"""
|
||||||
event = models.ForeignKey(Event, verbose_name='关联事件',
|
event = models.ForeignKey(Event, verbose_name='关联事件',
|
||||||
on_delete=models.CASCADE)
|
on_delete=models.CASCADE)
|
||||||
pusher = models.ForeignKey(User, verbose_name='推送人',
|
recipient = models.ForeignKey(User, verbose_name='接收人',
|
||||||
on_delete=models.CASCADE)
|
on_delete=models.CASCADE)
|
||||||
push_setting = models.ForeignKey(PushSetting, verbose_name='通过哪个配置',
|
notify_setting = models.ForeignKey(NotifySetting, verbose_name='通过哪个配置',
|
||||||
on_delete=models.CASCADE, null=True, blank=True)
|
on_delete=models.CASCADE, null=True, blank=True)
|
||||||
post = models.ForeignKey(Post, verbose_name='岗位',
|
post = models.ForeignKey(Post, verbose_name='岗位',
|
||||||
on_delete=models.CASCADE, null=True, blank=True)
|
on_delete=models.CASCADE, null=True, blank=True)
|
||||||
dept = models.ForeignKey(Dept, verbose_name='部门',
|
dept = models.ForeignKey(Dept, verbose_name='部门',
|
||||||
on_delete=models.CASCADE, null=True, blank=True)
|
on_delete=models.CASCADE, null=True, blank=True)
|
||||||
msg = models.TextField('推送文本')
|
msg = models.TextField('推送文本', null=True, blank=True)
|
||||||
is_read = models.BooleanField('站内信已读', default=False)
|
is_read = models.BooleanField('站内信已读', default=False)
|
||||||
is_sms_send = models.BooleanField('短信已发送', default=False)
|
|
||||||
can_handle = models.BooleanField('是否可处理', default=False)
|
can_handle = models.BooleanField('是否可处理', default=False)
|
||||||
last_push_time = models.DateTimeField('最后推送时间', default=timezone.now)
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from apps.am.serializers import AreaSimpleSerializer
|
from apps.am.serializers import AreaSimpleSerializer
|
||||||
from apps.ecm.models import EventCate, Push, PushSetting, Event
|
from apps.ecm.models import EventCate, Remind, NotifySetting, Event
|
||||||
from apps.utils.serializers import CustomModelSerializer
|
from apps.utils.serializers import CustomModelSerializer
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from apps.system.serializers import UserSimpleSerializer
|
from apps.system.serializers import UserSimpleSerializer
|
||||||
|
@ -23,9 +23,9 @@ class EventCateUpdateSerializer(CustomModelSerializer):
|
||||||
fields = ['speaker_on', 'speakers', 'filter_area_level']
|
fields = ['speaker_on', 'speakers', 'filter_area_level']
|
||||||
|
|
||||||
|
|
||||||
class PushSettingsSerializer(CustomModelSerializer):
|
class NotifySettingsSerializer(CustomModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = PushSetting
|
model = NotifySetting
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
|
|
||||||
|
|
||||||
|
@ -46,9 +46,9 @@ class EventHandleSerializer(CustomModelSerializer):
|
||||||
fields = ['handle_desc']
|
fields = ['handle_desc']
|
||||||
|
|
||||||
|
|
||||||
class PushSerializer(CustomModelSerializer):
|
class RemindSerializer(CustomModelSerializer):
|
||||||
pusher_ = UserSimpleSerializer(source='pusher', read_only=True)
|
recipient_ = UserSimpleSerializer(source='recipient', read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Push
|
model = Remind
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from apps.ecm.views import EventCateViewSet, PushSettingViewSet, EventViewSet, PushViewSet
|
from apps.ecm.views import EventCateViewSet, NotifySettingViewSet, EventViewSet, RemindViewSet
|
||||||
from django.urls import path, include
|
from django.urls import path, include
|
||||||
from rest_framework.routers import DefaultRouter
|
from rest_framework.routers import DefaultRouter
|
||||||
|
|
||||||
|
@ -8,8 +8,8 @@ HTML_BASE_URL = 'ecm/'
|
||||||
router = DefaultRouter()
|
router = DefaultRouter()
|
||||||
router.register('event_cate', EventCateViewSet, basename='event_cate')
|
router.register('event_cate', EventCateViewSet, basename='event_cate')
|
||||||
router.register('event', EventViewSet, basename='event')
|
router.register('event', EventViewSet, basename='event')
|
||||||
router.register('push_setting', PushSettingViewSet, basename='push_setting')
|
router.register('notify_setting', NotifySettingViewSet, basename='notify_setting')
|
||||||
router.register('push', PushViewSet, basename='push')
|
router.register('remind', RemindViewSet, basename='remind')
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path(API_BASE_URL, include(router.urls)),
|
path(API_BASE_URL, include(router.urls)),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
|
|
||||||
from apps.ecm.models import Event, EventCate, Push, PushSetting
|
from apps.ecm.models import Event, EventCate, Remind, NotifySetting
|
||||||
from apps.ecm.serializers import (EventCateListSerializer, EventCateUpdateSerializer, EventHandleSerializer,
|
from apps.ecm.serializers import (EventCateListSerializer, EventCateUpdateSerializer, EventHandleSerializer,
|
||||||
EventSerializer, PushSerializer, PushSettingsSerializer)
|
EventSerializer, RemindSerializer, NotifySettingsSerializer)
|
||||||
from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet
|
from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet
|
||||||
from rest_framework.mixins import UpdateModelMixin, ListModelMixin, RetrieveModelMixin
|
from rest_framework.mixins import UpdateModelMixin, ListModelMixin, RetrieveModelMixin
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
|
@ -19,9 +19,9 @@ class EventCateViewSet(UpdateModelMixin, ListModelMixin, CustomGenericViewSet):
|
||||||
update_serializer_class = EventCateUpdateSerializer
|
update_serializer_class = EventCateUpdateSerializer
|
||||||
|
|
||||||
|
|
||||||
class PushSettingViewSet(CustomModelViewSet):
|
class NotifySettingViewSet(CustomModelViewSet):
|
||||||
queryset = PushSetting.objects.all()
|
queryset = NotifySetting.objects.all()
|
||||||
serializer_class = PushSettingsSerializer
|
serializer_class = NotifySettingsSerializer
|
||||||
|
|
||||||
|
|
||||||
class EventViewSet(ListModelMixin, RetrieveModelMixin, CustomGenericViewSet):
|
class EventViewSet(ListModelMixin, RetrieveModelMixin, CustomGenericViewSet):
|
||||||
|
@ -45,10 +45,10 @@ class EventViewSet(ListModelMixin, RetrieveModelMixin, CustomGenericViewSet):
|
||||||
return Response()
|
return Response()
|
||||||
|
|
||||||
|
|
||||||
class PushViewSet(ListModelMixin, CustomGenericViewSet):
|
class RemindViewSet(ListModelMixin, CustomGenericViewSet):
|
||||||
perms_map = {'get': 'push:view'}
|
perms_map = {'get': 'envent:view'}
|
||||||
queryset = Push.objects.all()
|
queryset = Remind.objects.all()
|
||||||
serializer_class = PushSerializer
|
serializer_class = RemindSerializer
|
||||||
|
|
||||||
@action(methods=['get'], detail=False, perms_map={'get': '*'})
|
@action(methods=['get'], detail=False, perms_map={'get': '*'})
|
||||||
def my(self, request, *args, **kwargs):
|
def my(self, request, *args, **kwargs):
|
||||||
|
@ -56,7 +56,7 @@ class PushViewSet(ListModelMixin, CustomGenericViewSet):
|
||||||
推送给我的
|
推送给我的
|
||||||
"""
|
"""
|
||||||
user = self.request.user
|
user = self.request.user
|
||||||
queryset = self.filter_queryset(self.get_queryset().filter(pusher=user))
|
queryset = self.filter_queryset(self.get_queryset().filter(recipient=user))
|
||||||
|
|
||||||
page = self.paginate_queryset(queryset)
|
page = self.paginate_queryset(queryset)
|
||||||
if page is not None:
|
if page is not None:
|
||||||
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
# Generated by Django 3.2.12 on 2022-06-14 05:00
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import django.utils.timezone
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('system', '0001_initial'),
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='NotWorkRemark',
|
||||||
|
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='删除标记')),
|
||||||
|
('not_work_date', models.DateField(verbose_name='未打卡日期')),
|
||||||
|
('remark', models.CharField(blank=True, max_length=200, null=True, verbose_name='未打卡说明')),
|
||||||
|
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='notworkremark_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='notworkremark_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
|
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='用户')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Employee',
|
||||||
|
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='删除标记')),
|
||||||
|
('type', models.CharField(choices=[('employee', '内部员工'), ('remployee', '相关方人员'), ('visitor', '访客')], default='employee', max_length=10, verbose_name='人员类型')),
|
||||||
|
('name', models.CharField(max_length=20, verbose_name='姓名')),
|
||||||
|
('phone', models.CharField(blank=True, max_length=11, null=True, unique=True, verbose_name='手机号')),
|
||||||
|
('email', models.EmailField(blank=True, max_length=254, null=True, verbose_name='邮箱号')),
|
||||||
|
('number', models.CharField(blank=True, max_length=50, null=True, verbose_name='人员编号')),
|
||||||
|
('photo', models.CharField(blank=True, max_length=1000, null=True, verbose_name='证件照')),
|
||||||
|
('id_number', models.CharField(blank=True, max_length=100, null=True, unique=True, verbose_name='身份证号')),
|
||||||
|
('gender', models.CharField(default='男', max_length=10, verbose_name='性别')),
|
||||||
|
('signature', models.CharField(blank=True, max_length=200, null=True, verbose_name='签名图片')),
|
||||||
|
('birthday', models.DateField(blank=True, null=True, verbose_name='出生年月日')),
|
||||||
|
('qualification', models.CharField(blank=True, max_length=50, null=True, verbose_name='学历')),
|
||||||
|
('job_state', models.IntegerField(choices=[(10, '在职'), (20, '离职')], default=1, verbose_name='在职状态')),
|
||||||
|
('face_data', models.JSONField(blank=True, null=True, verbose_name='人脸识别数据')),
|
||||||
|
('is_atwork', models.BooleanField(default=False, verbose_name='当前在岗')),
|
||||||
|
('show_atwork', models.BooleanField(default=True, verbose_name='是否展示在岗状态')),
|
||||||
|
('last_check_time', models.DateTimeField(blank=True, null=True, verbose_name='打卡时间')),
|
||||||
|
('not_work_remark', models.CharField(blank=True, max_length=200, null=True, verbose_name='当前未打卡说明')),
|
||||||
|
('third_info', models.JSONField(blank=True, default=dict, verbose_name='三方信息')),
|
||||||
|
('belong_dept', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='employee_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='employee_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='employee_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
|
||||||
|
('user', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL, verbose_name='系统账号')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': '员工补充信息',
|
||||||
|
'verbose_name_plural': '员工补充信息',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='ClockRecord',
|
||||||
|
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='删除标记')),
|
||||||
|
('type', models.PositiveSmallIntegerField(choices=[(10, '上班打卡')], default=10, verbose_name='打卡类型')),
|
||||||
|
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='clockrecord_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='clockrecord_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Certificate',
|
||||||
|
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='证书名称')),
|
||||||
|
('number', models.CharField(max_length=20, unique=True, verbose_name='证书编号')),
|
||||||
|
('type', models.PositiveSmallIntegerField(default=10, verbose_name='证书类型')),
|
||||||
|
('issue_date', models.DateField(verbose_name='发证日期')),
|
||||||
|
('expiration_date', models.DateField(verbose_name='有效期')),
|
||||||
|
('review_date', models.DateField(verbose_name='下一次复审日期')),
|
||||||
|
('file', 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='certificate_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
|
||||||
|
('employee', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='hrm.employee', verbose_name='对应人员')),
|
||||||
|
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='certificate_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,22 @@
|
||||||
|
# Generated by Django 3.2.12 on 2022-06-17 03:24
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('hrm', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='employee',
|
||||||
|
name='face_data',
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='employee',
|
||||||
|
name='job_state',
|
||||||
|
field=models.IntegerField(choices=[(10, '在职'), (20, '离职')], default=10, verbose_name='在职状态'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -24,17 +24,16 @@ class Employee(CommonBModel):
|
||||||
verbose_name='系统账号',
|
verbose_name='系统账号',
|
||||||
on_delete=models.PROTECT, null=True, blank=True)
|
on_delete=models.PROTECT, null=True, blank=True)
|
||||||
name = models.CharField('姓名', max_length=20)
|
name = models.CharField('姓名', max_length=20)
|
||||||
phone = models.CharField('手机号', max_length=11, null=True, blank=True)
|
phone = models.CharField('手机号', max_length=11, null=True, blank=True, unique=True)
|
||||||
email = models.EmailField('邮箱号', null=True, blank=True)
|
email = models.EmailField('邮箱号', null=True, blank=True)
|
||||||
number = models.CharField('人员编号', max_length=50, null=True, blank=True)
|
number = models.CharField('人员编号', max_length=50, null=True, blank=True)
|
||||||
photo = models.CharField('证件照', max_length=1000, 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)
|
id_number = models.CharField('身份证号', max_length=100, null=True, blank=True, unique=True)
|
||||||
gender = models.CharField('性别', max_length=10, default='男')
|
gender = models.CharField('性别', max_length=10, default='男')
|
||||||
signature = models.CharField('签名图片', max_length=200, null=True, blank=True)
|
signature = models.CharField('签名图片', max_length=200, null=True, blank=True)
|
||||||
birthday = models.DateField('出生年月日', null=True, blank=True)
|
birthday = models.DateField('出生年月日', null=True, blank=True)
|
||||||
qualification = models.CharField('学历', max_length=50, null=True, blank=True)
|
qualification = models.CharField('学历', max_length=50, null=True, blank=True)
|
||||||
job_state = models.IntegerField('在职状态', choices=jobstate_choices, default=1)
|
job_state = models.IntegerField('在职状态', choices=jobstate_choices, default=10)
|
||||||
face_data = models.JSONField('人脸识别数据', null=True, blank=True)
|
|
||||||
is_atwork = models.BooleanField('当前在岗', default=False)
|
is_atwork = models.BooleanField('当前在岗', default=False)
|
||||||
show_atwork = models.BooleanField('是否展示在岗状态', default=True)
|
show_atwork = models.BooleanField('是否展示在岗状态', default=True)
|
||||||
last_check_time = models.DateTimeField('打卡时间', null=True, blank=True)
|
last_check_time = models.DateTimeField('打卡时间', null=True, blank=True)
|
||||||
|
@ -88,9 +87,9 @@ class Certificate(CommonAModel):
|
||||||
)
|
)
|
||||||
employee = models.ForeignKey(Employee, verbose_name='对应人员', on_delete=models.CASCADE)
|
employee = models.ForeignKey(Employee, verbose_name='对应人员', on_delete=models.CASCADE)
|
||||||
name = models.CharField('证书名称', max_length=20)
|
name = models.CharField('证书名称', max_length=20)
|
||||||
number = models.CharField('证书编号', max_length=20)
|
number = models.CharField('证书编号', max_length=20, unique=True)
|
||||||
type = models.PositiveSmallIntegerField('证书类型', default=10)
|
type = models.PositiveSmallIntegerField('证书类型', default=10)
|
||||||
issue_date = models.DateField('发证日期')
|
issue_date = models.DateField('发证日期')
|
||||||
expiration_date = models.DateField('有效期')
|
expiration_date = models.DateField('有效期')
|
||||||
review_date = models.DateField('下一次复审日期')
|
review_date = models.DateField('下一次复审日期')
|
||||||
file = models.CharField('文件地址', max_length=1000, null=True, blank=True)
|
file = models.TextField('文件地址', null=True, blank=True)
|
||||||
|
|
|
@ -12,6 +12,7 @@ from apps.third.clients import dhClient
|
||||||
from apps.third.tapis import dhapis
|
from apps.third.tapis import dhapis
|
||||||
import re
|
import re
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
|
||||||
class EmployeeBaseSerializer(CustomModelSerializer):
|
class EmployeeBaseSerializer(CustomModelSerializer):
|
||||||
|
@ -35,8 +36,7 @@ class EmployeeCreateUpdateSerializer(EmployeeBaseSerializer):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Employee
|
model = Employee
|
||||||
exclude = EXCLUDE_FIELDS + ['face_data',
|
exclude = EXCLUDE_FIELDS + ['is_atwork', 'last_check_time',
|
||||||
'is_atwork', 'last_check_time',
|
|
||||||
'not_work_remark', 'third_info', 'type']
|
'not_work_remark', 'third_info', 'type']
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
'phone': {'required': True},
|
'phone': {'required': True},
|
||||||
|
@ -48,7 +48,7 @@ class EmployeeCreateUpdateSerializer(EmployeeBaseSerializer):
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
instance = super().create(validated_data)
|
instance = super().create(validated_data)
|
||||||
if dhClient:
|
if settings.DAHUA_ENABLED and dhClient:
|
||||||
# 创建人员
|
# 创建人员
|
||||||
_, res = dhClient.request(**dhapis['person_gen_id'])
|
_, res = dhClient.request(**dhapis['person_gen_id'])
|
||||||
personId = res['id']
|
personId = res['id']
|
||||||
|
@ -118,7 +118,7 @@ class EmployeeCreateUpdateSerializer(EmployeeBaseSerializer):
|
||||||
departmentId = instance.belong_dept.third_info['dh_id']
|
departmentId = instance.belong_dept.third_info['dh_id']
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
if dhClient:
|
if settings.DAHUA_ENABLED and dhClient:
|
||||||
third_info = instance.third_info
|
third_info = instance.third_info
|
||||||
dh_id = instance.third_info['dh_id']
|
dh_id = instance.third_info['dh_id']
|
||||||
dh_photo = third_info['dh_photo']
|
dh_photo = third_info['dh_photo']
|
||||||
|
@ -192,7 +192,7 @@ class EmployeeSerializer(EmployeeBaseSerializer):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Employee
|
model = Employee
|
||||||
exclude = ['face_data']
|
fields = '__all__'
|
||||||
read_only_fields = ['is_atwork', 'last_check_time', 'not_work_remark']
|
read_only_fields = ['is_atwork', 'last_check_time', 'not_work_remark']
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -8,9 +8,17 @@ from apps.hrm.models import Employee
|
||||||
def updateEmployee(sender, instance, created, **kwargs):
|
def updateEmployee(sender, instance, created, **kwargs):
|
||||||
# if created:
|
# if created:
|
||||||
# 如果账号所属部门有变动, 更新关联人员的所属部门, 只限内部人员
|
# 如果账号所属部门有变动, 更新关联人员的所属部门, 只限内部人员
|
||||||
if not instance.is_superuser:
|
if not instance.is_superuser and instance.type == 'employee':
|
||||||
ep = Employee.objects.filter(user=instance).first()
|
ep = Employee.objects.filter(user=instance).first()
|
||||||
if ep and ep.type == 'employee':
|
if ep:
|
||||||
if ep.belong_dept and ep.belong_dept != instance.belong_dept:
|
if ep.belong_dept and ep.belong_dept != instance.belong_dept:
|
||||||
ep.belong_dept = instance.belong_dept
|
ep.belong_dept = instance.belong_dept
|
||||||
ep.save()
|
ep.save()
|
||||||
|
else:
|
||||||
|
Employee.objects.get_or_create(user=instance,
|
||||||
|
defaults={
|
||||||
|
"user": instance,
|
||||||
|
"name": instance.name,
|
||||||
|
"phone": instance.phone,
|
||||||
|
"belong_dept": instance.belong_dept
|
||||||
|
})
|
||||||
|
|
|
@ -30,7 +30,7 @@ class EmployeeViewSet(CustomModelViewSet):
|
||||||
"""
|
"""
|
||||||
人员管理
|
人员管理
|
||||||
"""
|
"""
|
||||||
queryset = Employee.objects.all()
|
queryset = Employee.objects.filter(type='employee')
|
||||||
select_related_fields = ['user']
|
select_related_fields = ['user']
|
||||||
filterset_class = EmployeeFilterSet
|
filterset_class = EmployeeFilterSet
|
||||||
serializer_class = EmployeeSerializer
|
serializer_class = EmployeeSerializer
|
||||||
|
|
|
@ -1,34 +1,38 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from apps.system.models import Dept, Dictionary, User
|
from apps.hrm.models import Certificate
|
||||||
from apps.utils.models import BaseModel, CommonAModel, CommonBModel
|
from apps.system.models import Dept, Dictionary, File, User
|
||||||
|
from apps.utils.models import BaseModel, CommonADModel, CommonAModel, CommonBDModel, CommonBModel
|
||||||
from apps.am.models import Area
|
from apps.am.models import Area
|
||||||
from apps.wf.models import Ticket, Workflow
|
from apps.wf.models import Ticket, Workflow
|
||||||
|
|
||||||
|
|
||||||
# Create your models here.
|
# Create your models here.
|
||||||
class OptCate(CommonAModel):
|
class OplCate(CommonAModel):
|
||||||
"""
|
"""
|
||||||
作业票种类
|
作业许可种类
|
||||||
"""
|
"""
|
||||||
code = models.CharField('标识', max_length=10, unique=True)
|
code = models.CharField('标识', max_length=10, unique=True)
|
||||||
name = models.CharField('名称', max_length=20, unique=True)
|
name = models.CharField('名称', max_length=20, unique=True)
|
||||||
|
description = models.TextField('描述', null=True, blank=True)
|
||||||
|
workflow = models.ForeignKey(Workflow, verbose_name='使用的工作流')
|
||||||
template_export = models.TextField('导出word模板', null=True, blank=True)
|
template_export = models.TextField('导出word模板', null=True, blank=True)
|
||||||
workflow = models.ForeignKey(Workflow, verbose_name='所用工作流',
|
risk_options = models.ManyToManyField(Dictionary, verbose_name='风险分析')
|
||||||
on_delete=models.CASCADE, null=True, blank=True)
|
measure_options = models.ManyToManyField(Dictionary, verbose_name='控制措施')
|
||||||
|
close_options = models.ManyToManyField(Dictionary, verbose_name='关闭工作')
|
||||||
|
|
||||||
|
|
||||||
class Operation(CommonBModel):
|
class Operation(CommonBDModel):
|
||||||
|
OP_CREATE = 10
|
||||||
|
OP_AUDIT = 20
|
||||||
|
OP_WAIT = 30
|
||||||
|
OP_WORK = 40
|
||||||
|
OP_CLOSE = 50
|
||||||
OP_STATE_CHOICES = (
|
OP_STATE_CHOICES = (
|
||||||
(10, '创建中'),
|
(OP_CREATE, '创建中'),
|
||||||
(20, '审批中'),
|
(OP_AUDIT, '审批中'),
|
||||||
(30, '待作业'),
|
(OP_WAIT, '待作业'),
|
||||||
(40, '待关闭'),
|
(OP_WORK, '作业中'),
|
||||||
(50, '已关闭')
|
(OP_CLOSE, '已关闭')
|
||||||
)
|
|
||||||
OP_WORK_CHOICES = (
|
|
||||||
('work', '运行'),
|
|
||||||
('stop', '停机'),
|
|
||||||
('repair', '检修')
|
|
||||||
)
|
)
|
||||||
number = models.CharField('作业编号', max_length=20, null=True, blank=True)
|
number = models.CharField('作业编号', max_length=20, null=True, blank=True)
|
||||||
name = models.CharField('作业简述', max_length=200)
|
name = models.CharField('作业简述', max_length=200)
|
||||||
|
@ -37,33 +41,109 @@ class Operation(CommonBModel):
|
||||||
place = models.TextField('具体地点', null=True, blank=True)
|
place = models.TextField('具体地点', null=True, blank=True)
|
||||||
start_time = models.DateTimeField('作业开始时间')
|
start_time = models.DateTimeField('作业开始时间')
|
||||||
end_time = models.DateTimeField('作业结束时间')
|
end_time = models.DateTimeField('作业结束时间')
|
||||||
dept_sd = models.ForeignKey(Dept, verbose_name='属地部门',
|
dept_ter = models.ForeignKey(Dept, verbose_name='属地部门',
|
||||||
on_delete=models.CASCADE)
|
on_delete=models.CASCADE)
|
||||||
dept_yw = models.ForeignKey(Dept, verbose_name='业务部门',
|
dept_bus = models.ForeignKey(Dept, verbose_name='业务部门',
|
||||||
on_delete=models.CASCADE)
|
on_delete=models.CASCADE)
|
||||||
user_xt = models.ForeignKey(User, verbose_name='业务部门协调员',
|
coordinator = models.ForeignKey(User, verbose_name='业务部门协调员',
|
||||||
on_delete=models.CASCADE)
|
on_delete=models.CASCADE)
|
||||||
user_fz = models.ForeignKey(User, verbose_name='作业负责人',
|
state_work = models.CharField('生产状态', max_length=20, help_text='运行/停机/检修')
|
||||||
on_delete=models.CASCADE)
|
|
||||||
user_jh = models.ForeignKey(User, verbose_name='作业监护人',
|
|
||||||
)
|
|
||||||
state_work = models.CharField('生产状态', choices=OP_WORK_CHOICES)
|
|
||||||
|
|
||||||
|
|
||||||
class Opt(CommonBModel):
|
class Opl(CommonBDModel):
|
||||||
"""
|
"""
|
||||||
作业票
|
作业许可证
|
||||||
"""
|
"""
|
||||||
|
CLOSE_CHOICES1 = (
|
||||||
|
(10, '作业正常结束'),
|
||||||
|
(20, '因计划改变停止作业'),
|
||||||
|
(30, '因发生异常终止作业'),
|
||||||
|
(40, '其他')
|
||||||
|
)
|
||||||
operation = models.ForeignKey(Operation, verbose_name='关联作业',
|
operation = models.ForeignKey(Operation, verbose_name='关联作业',
|
||||||
on_delete=models.CASCADE)
|
on_delete=models.CASCADE, related_name='opl_operation')
|
||||||
number = models.CharField('作业票编号', max_length=20, null=True, blank=True)
|
number = models.CharField('作业许可编号', max_length=20, null=True, blank=True)
|
||||||
cate = models.ForeignKey(OptCate, verbose_name='作业票种类',
|
level = models.CharField('作业级别', max_length=20,
|
||||||
|
help_text='特技/三级/二级/主干道/次干道', null=True, blank=True)
|
||||||
|
cate = models.ForeignKey(OplCate, verbose_name='作业许可种类',
|
||||||
on_delete=models.CASCADE)
|
on_delete=models.CASCADE)
|
||||||
dept_zy = models.ForeignKey(Dept, verbose_name='作业部门',
|
start_time = models.DateTimeField('作业开始时间', null=True, blank=True)
|
||||||
|
end_time = models.DateTimeField('作业结束时间', null=True, blank=True)
|
||||||
|
dept_do = models.ForeignKey(Dept, verbose_name='作业部门',
|
||||||
on_delete=models.CASCADE)
|
on_delete=models.CASCADE)
|
||||||
user_fz = models.ForeignKey(User, verbose_name='作业负责人',
|
charger = models.ForeignKey(User, verbose_name='作业负责人',
|
||||||
on_delete=models.CASCADE)
|
on_delete=models.CASCADE, related_name='opl_charger')
|
||||||
user_jh = models.ForeignKey(User, verbose_name='作业监护人',
|
monitor = models.ForeignKey(User, verbose_name='作业监护人',
|
||||||
)
|
on_delete=models.CASCADE, related_name='opl_monitor')
|
||||||
ticket = models.ForeignKey(Ticket, verbose_name='关联工单',
|
# electrician = models.ForeignKey(User, verbose_name='电工',
|
||||||
on_delete=models.CASCADE, null=True, blank=True)
|
# on_delete=models.CASCADE,
|
||||||
|
# related_name='opl_electrician', null=True, blank=True)
|
||||||
|
work_time = models.DateTimeField('安装/拆除时间', null=True, blank=True)
|
||||||
|
accept_time = models.DateTimeField('验收时间', null=True, blank=True)
|
||||||
|
work_type = models.CharField('安装/拆除', max_length=20, null=True, blank=True)
|
||||||
|
power = models.PositiveIntegerField('用电功率', null=True, blank=True)
|
||||||
|
power_to = models.CharField('用电地点', max_length=20, null=True, blank=True)
|
||||||
|
power_from = models.CharField('电源接入点', max_length=20, null=True, blank=True)
|
||||||
|
power_end_time = models.DateTimeField('用电截至', null=True, blank=True)
|
||||||
|
power_start_time = models.DateTimeField('用电开始', null=True, blank=True)
|
||||||
|
power_days = models.PositiveSmallIntegerField('计划用电天数', null=True, blank=True)
|
||||||
|
other_risk = models.TextField('其他风险', null=True, blank=True)
|
||||||
|
other_emr = models.TextField('其他应急处置', null=True, blank=True)
|
||||||
|
escape_route = models.TextField('逃生路径', null=True, blank=True)
|
||||||
|
risks_checked = models.JSONField('风险选择', default=list, null=False, blank=True)
|
||||||
|
measures_checked = models.JSONField('措施选择', default=list, null=False, blank=True)
|
||||||
|
workers = models.ManyToManyField(User, verbose_name='作业人员', through='opm.oplworker')
|
||||||
|
create_imgs = models.ManyToManyField(File, verbose_name='作业审批照片')
|
||||||
|
close_imgs = models.ManyToManyField(File, verbose_name='作业关闭照片')
|
||||||
|
close_note = models.PositiveSmallIntegerField('作业关闭情况',
|
||||||
|
choices=CLOSE_CHOICES1,
|
||||||
|
help_text='正常结束/计划改变停止/发生异常停止/其他',
|
||||||
|
null=True, blank=True)
|
||||||
|
close_dos = models.JSONField('关闭工作', default=list, null=False, blank=True)
|
||||||
|
close_desc = models.TextField('作业关闭描述', null=True, blank=True)
|
||||||
|
ticket = models.OneToOneField(Ticket, verbose_name='关联工单',
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
null=True, blank=True)
|
||||||
|
|
||||||
|
|
||||||
|
class OplWorker(BaseModel):
|
||||||
|
"""
|
||||||
|
作业许可人员
|
||||||
|
"""
|
||||||
|
worker = models.ForeignKey(User, verbose_name='作业人员',
|
||||||
|
on_delete=models.CASCADE)
|
||||||
|
opl = models.ForeignKey(Opl, verbose_name='作业许可',
|
||||||
|
on_delete=models.CASCADE)
|
||||||
|
duty = models.CharField('工作职责', null=True, blank=True, help_text='作业人员/起重司机/司索人员/起重指挥/电工')
|
||||||
|
certificates = models.ManyToManyField(Certificate, verbose_name='关联证书', through='opt.optcert')
|
||||||
|
|
||||||
|
|
||||||
|
class OplCert(BaseModel):
|
||||||
|
"""
|
||||||
|
作业许可人员证书
|
||||||
|
"""
|
||||||
|
opl_worker = models.ForeignKey(OplWorker, verbose_name='关联作业人员',
|
||||||
|
on_delete=models.CASCADE)
|
||||||
|
certificate = models.ForeignKey(Certificate, 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('有效期')
|
||||||
|
review_date = models.DateField('下一次复审日期')
|
||||||
|
file = models.TextField('文件地址', null=True, blank=True)
|
||||||
|
|
||||||
|
|
||||||
|
class GasCheck(CommonADModel):
|
||||||
|
"""
|
||||||
|
气体检测记录
|
||||||
|
"""
|
||||||
|
check_time = models.DateTimeField('检测时间')
|
||||||
|
check_place = models.CharField('检测部位')
|
||||||
|
o2 = models.DecimalField(decimal_places=1, max_digits=3)
|
||||||
|
co = models.PositiveSmallIntegerField()
|
||||||
|
lel = models.DecimalField(decimal_places=1, max_digits=3)
|
||||||
|
is_ok = models.BooleanField('是否正常', default=True)
|
||||||
|
checker = models.ForeignKey(User, verbose_name='检测人')
|
||||||
|
opl = models.ForeignKey(Opl, verbose_name='关联作业许可')
|
||||||
|
|
|
@ -1,30 +1,118 @@
|
||||||
from apps.opm.models import Operation, OptCate
|
from apps.hrm.models import Certificate
|
||||||
|
from apps.opm.models import GasCheck, Operation, Opl, OplCate, OplCert, OplWorker
|
||||||
from apps.utils.serializers import CustomModelSerializer
|
from apps.utils.serializers import CustomModelSerializer
|
||||||
from apps.wf.serializers import WorkflowSimpleSerializer
|
|
||||||
from apps.utils.constants import EXCLUDE_FIELDS
|
from apps.utils.constants import EXCLUDE_FIELDS
|
||||||
|
from rest_framework import serializers
|
||||||
|
from django.db import transaction
|
||||||
|
from rest_framework.exceptions import ParseError
|
||||||
|
|
||||||
|
|
||||||
class OptCateCreateUpdateSerializer(CustomModelSerializer):
|
class OplCateCreateUpdateSerializer(CustomModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = OptCate
|
model = OplCate
|
||||||
fields = ['code', 'name', 'template_export', 'workflow']
|
fields = ['code', 'name', 'template_export', 'workflow']
|
||||||
|
|
||||||
|
|
||||||
class OptCateSerializer(CustomModelSerializer):
|
class OplCateSerializer(CustomModelSerializer):
|
||||||
workflow_ = WorkflowSimpleSerializer(source='workflow', read_only=True)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = OptCate
|
model = OplCate
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
|
|
||||||
|
|
||||||
class OperationCreateUpdateSerializer(CustomModelSerializer):
|
class OperationCreateUpdateSerializer(CustomModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Operation
|
model = Operation
|
||||||
fields = EXCLUDE_FIELDS + ['number']
|
exclude = EXCLUDE_FIELDS + ['number']
|
||||||
|
|
||||||
|
|
||||||
class OperationSerializer(CustomModelSerializer):
|
class OperationSerializer(CustomModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Operation
|
model = Operation
|
||||||
fields = "__all__"
|
fields = "__all__"
|
||||||
|
|
||||||
|
|
||||||
|
class OplWorkerCreateSerializer(CustomModelSerializer):
|
||||||
|
certificates = serializers.PrimaryKeyRelatedField(label='证书ID', many=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = OplWorker
|
||||||
|
fields = ['worker', 'duty', 'certificates', 'opl']
|
||||||
|
|
||||||
|
def create(self, validated_data):
|
||||||
|
certificates = validated_data.pop('certificates')
|
||||||
|
if OplWorker.objects.filter(worker=validated_data['worker'], opl=validated_data['opl']).exists():
|
||||||
|
raise ParseError('该成员已加入')
|
||||||
|
with transaction.atomic():
|
||||||
|
oplw = super().create(validated_data)
|
||||||
|
for x in certificates:
|
||||||
|
oplc, _ = OplCert.objects.get_or_create(opl_worker=oplw, certificate=x,
|
||||||
|
defaults={'opl_worker': oplw, 'certificate': x})
|
||||||
|
for f in Certificate._meta.fields:
|
||||||
|
if f.name not in ['id']:
|
||||||
|
setattr(oplc, f.name, getattr(x, f.name, None))
|
||||||
|
oplc.save()
|
||||||
|
return oplw
|
||||||
|
|
||||||
|
|
||||||
|
class OplWorkerUpdateSerializer(CustomModelSerializer):
|
||||||
|
certificates = serializers.PrimaryKeyRelatedField(label='证书ID', many=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = OplWorker
|
||||||
|
fields = ['duty', 'certificates']
|
||||||
|
|
||||||
|
def update(self, instance, validated_data):
|
||||||
|
certificates = validated_data.pop('certificates')
|
||||||
|
with transaction.atomic():
|
||||||
|
oplw = super().update(instance, validated_data)
|
||||||
|
for x in certificates:
|
||||||
|
oplc, _ = OplCert.objects.get_or_create(opl_worker=oplw, certificate=x,
|
||||||
|
defaults={'opl_worker': oplw, 'certificate': x})
|
||||||
|
for f in Certificate._meta.fields:
|
||||||
|
if f.name not in ['id']:
|
||||||
|
setattr(oplc, f.name, getattr(x, f.name, None))
|
||||||
|
oplc.save()
|
||||||
|
return oplw
|
||||||
|
|
||||||
|
|
||||||
|
class OplWorkerSerializer():
|
||||||
|
class Meta:
|
||||||
|
model = OplWorker
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
|
||||||
|
class GasCheckCreateUpdateSerializer(CustomModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = GasCheck
|
||||||
|
exclude = EXCLUDE_FIELDS
|
||||||
|
|
||||||
|
|
||||||
|
class GasCheckSerializer(CustomModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = GasCheck
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
|
||||||
|
class OplCreateUpdateSerializer(CustomModelSerializer):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Opl
|
||||||
|
fields = ['operation', 'level', 'cate', 'start_time', 'end_time',
|
||||||
|
'dept_do', 'charger', 'monitor', 'work_time', 'work_type',
|
||||||
|
'accept_time', 'power', 'power_to', 'power_from',
|
||||||
|
'power_end_time', 'power_start_time', 'power_days',
|
||||||
|
'other_risk', 'other_emr', 'escape_route',
|
||||||
|
'risks_checked', 'measures_checked', 'create_imgs']
|
||||||
|
|
||||||
|
|
||||||
|
class OplSerializer(CustomModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Opl
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
|
||||||
|
class OplCloseSerializer(CustomModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Opl
|
||||||
|
fields = ['close_imgs', 'close_note', 'close_dos', 'close_desc']
|
||||||
|
|
|
@ -1,5 +1,28 @@
|
||||||
from apps.wf.models import Ticket
|
from apps.opm.models import Operation, Opl
|
||||||
|
from apps.opm.serializers import OplCloseSerializer
|
||||||
|
from apps.wf.models import Ticket, Transition
|
||||||
|
|
||||||
|
|
||||||
def create_opt(ticket: Ticket):
|
def bind_opl(ticket: Ticket, transition: Transition, new_ticket_data: dict):
|
||||||
pass
|
opl = Opl.objects.get(id=new_ticket_data['opl'])
|
||||||
|
ticket_data = ticket.ticket_data
|
||||||
|
ticket_data.update({'level': opl.level})
|
||||||
|
ticket.ticket_data = ticket_data
|
||||||
|
ticket.save()
|
||||||
|
opl.ticket = opl
|
||||||
|
opl.number = ticket.sn
|
||||||
|
opl.save()
|
||||||
|
|
||||||
|
|
||||||
|
def close_opl_submit(ticket: Ticket, transition: Transition, new_ticket_data: dict):
|
||||||
|
opl = Opl.objects.get(ticket=ticket)
|
||||||
|
serializer = OplCloseSerializer(instance=opl, data=new_ticket_data, partial=True)
|
||||||
|
serializer.is_valid(raise_exception=True)
|
||||||
|
serializer.save()
|
||||||
|
|
||||||
|
|
||||||
|
def give_perm_by_opl():
|
||||||
|
"""
|
||||||
|
根据许可证授予区域进入权限
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
# Create your tasks here
|
||||||
|
from __future__ import absolute_import, unicode_literals
|
||||||
|
from apps.opm.models import Operation, Opl
|
||||||
|
from apps.opm.services import give_perm_by_opl
|
||||||
|
from apps.utils.task import CustomTask
|
||||||
|
from celery import shared_task
|
||||||
|
|
||||||
|
from apps.wf.models import State, Ticket
|
||||||
|
|
||||||
|
|
||||||
|
@shared_task(base=CustomTask)
|
||||||
|
def opl_end(ticket_id):
|
||||||
|
"""
|
||||||
|
作业关闭时执行
|
||||||
|
"""
|
||||||
|
ticket = Ticket.objects.get(id=ticket_id)
|
||||||
|
operation = ticket.opl.operation
|
||||||
|
opls = Opl.objects.filter(operation=operation)
|
||||||
|
opls.filter(ticket=None).delete() # 删除无用许可证
|
||||||
|
states = opls.values_list('ticket__state__type', flat=True)
|
||||||
|
if 0 in states or 1 in states:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
operation.state = Operation.OP_CLOSE
|
||||||
|
operation.save()
|
||||||
|
|
||||||
|
|
||||||
|
@shared_task(base=CustomTask)
|
||||||
|
def opl_audit_start(ticket_id):
|
||||||
|
operation = Opl.objects.get(ticket__id=ticket_id).operation
|
||||||
|
if operation.state == Operation.OP_CREATE:
|
||||||
|
operation.state = Operation.OP_AUDIT
|
||||||
|
operation.save()
|
||||||
|
|
||||||
|
|
||||||
|
@shared_task(base=CustomTask)
|
||||||
|
def opl_audit_end(ticket_id):
|
||||||
|
opl = Opl.objects.get(ticket__id=ticket_id)
|
||||||
|
operation = opl.operation
|
||||||
|
if operation.state == Operation.OP_AUDIT:
|
||||||
|
operation.state = Operation.OP_WORK
|
||||||
|
operation.save()
|
||||||
|
# 授予区域或围栏权限
|
||||||
|
give_perm_by_opl(opl)
|
|
@ -1,19 +1,108 @@
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from apps.opm.models import Operation, OptCate
|
from rest_framework.response import Response
|
||||||
from apps.opm.serializers import OperationCreateUpdateSerializer, OptCateCreateUpdateSerializer, OptCateSerializer
|
from apps.opm.models import GasCheck, Operation, Opl, OplCate, OplWorker
|
||||||
from apps.utils.viewsets import CustomModelViewSet
|
from apps.opm.serializers import GasCheckCreateUpdateSerializer, GasCheckSerializer, OperationCreateUpdateSerializer, OperationSerializer, OplCateCreateUpdateSerializer, OplCateSerializer, OplSerializer, OplWorkerCreateSerializer, OplWorkerUpdateSerializer
|
||||||
|
from apps.opm.services import create_opl_ticket
|
||||||
|
from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet
|
||||||
|
from rest_framework.exceptions import ParseError
|
||||||
|
from rest_framework.mixins import CreateModelMixin, ListModelMixin, DestroyModelMixin
|
||||||
|
from rest_framework.decorators import action
|
||||||
|
from rest_framework import serializers
|
||||||
|
from django.db import transaction
|
||||||
|
|
||||||
# Create your views here.
|
# Create your views here.
|
||||||
class OptCateViewSet(CustomModelViewSet):
|
class OplCateViewSet(CustomModelViewSet):
|
||||||
queryset = OptCate.objects.all()
|
queryset = OplCate.objects.all()
|
||||||
create_serializer_class = OptCateCreateUpdateSerializer
|
create_serializer_class = OplCateCreateUpdateSerializer
|
||||||
update_serializer_class = OptCateCreateUpdateSerializer
|
update_serializer_class = OplCateCreateUpdateSerializer
|
||||||
serializer_class = OptCateSerializer
|
serializer_class = OplCateSerializer
|
||||||
|
|
||||||
|
|
||||||
class OperationViewSet(CustomModelViewSet):
|
class OperationViewSet(CustomModelViewSet):
|
||||||
queryset = Operation.objects.all()
|
queryset = Operation.objects.all()
|
||||||
create_serializer_class = OperationCreateUpdateSerializer
|
create_serializer_class = OperationCreateUpdateSerializer
|
||||||
update_serializer_class = OperationCreateUpdateSerializer
|
update_serializer_class = OperationCreateUpdateSerializer
|
||||||
|
serializer_class = OperationSerializer
|
||||||
|
|
||||||
|
def update(self, request, *args, **kwargs):
|
||||||
|
obj = self.get_object()
|
||||||
|
if obj.state != Operation.OP_CREATE:
|
||||||
|
raise ParseError('非创建状态不可修改')
|
||||||
|
return super().update(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def destroy(self, request, *args, **kwargs):
|
||||||
|
obj = self.get_object()
|
||||||
|
if obj.state != Operation.OP_CREATE:
|
||||||
|
raise ParseError('非创建状态不可删除')
|
||||||
|
obj.delete(soft=False)
|
||||||
|
return Response(status=204)
|
||||||
|
|
||||||
|
|
||||||
|
class OplViewSet(CustomModelViewSet):
|
||||||
|
queryset = Opl.objects.all()
|
||||||
|
create_serializer_class = OplCateCreateUpdateSerializer
|
||||||
|
update_serializer_class = OplCateCreateUpdateSerializer
|
||||||
|
serializer_class = OplSerializer
|
||||||
|
|
||||||
|
def destroy(self, request, *args, **kwargs):
|
||||||
|
obj = self.get_object()
|
||||||
|
if obj.ticket:
|
||||||
|
raise ParseError('许可证已处理不可删除')
|
||||||
|
return super().destroy(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def create(self, request, *args, **kwargs):
|
||||||
|
obj = self.get_object()
|
||||||
|
if obj.operation.state == Operation.OP_CLOSE:
|
||||||
|
raise ParseError('作业已关闭不可创建许可')
|
||||||
|
return super().create(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def update(self, request, *args, **kwargs):
|
||||||
|
obj = self.get_object()
|
||||||
|
if obj.ticket:
|
||||||
|
raise ParseError('许可证已处理不可编辑')
|
||||||
|
return super().update(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class OplWorkerViewSet(CustomModelViewSet):
|
||||||
|
perms_map = {'get': '*', 'post': 'opl:update', 'put': 'opl:update', 'delete': 'opl:update'}
|
||||||
|
queryset = OplWorker.objects.all()
|
||||||
|
create_serializer_class = OplWorkerCreateSerializer
|
||||||
|
update_serializer_class = OplWorkerUpdateSerializer
|
||||||
|
serializer_class = OplSerializer
|
||||||
|
|
||||||
|
def create(self, request, *args, **kwargs):
|
||||||
|
obj = self.get_object()
|
||||||
|
if obj.opl.ticket:
|
||||||
|
raise ParseError('许可证已处理不可新增')
|
||||||
|
return super().create(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def destroy(self, request, *args, **kwargs):
|
||||||
|
obj = self.get_object()
|
||||||
|
if obj.opl.ticket:
|
||||||
|
raise ParseError('许可证已处理不可删除')
|
||||||
|
return super().destroy(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def update(self, request, *args, **kwargs):
|
||||||
|
obj = self.get_object()
|
||||||
|
if obj.opl.ticket:
|
||||||
|
raise ParseError('许可证已处理不可编辑')
|
||||||
|
return super().update(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class GasCheckViewSet(CreateModelMixin, ListModelMixin, DestroyModelMixin, CustomGenericViewSet):
|
||||||
|
perms_map = {'get': '*', 'post': 'opl:update', 'delete': 'opl:update'}
|
||||||
|
queryset = GasCheck.objects.all()
|
||||||
|
create_serializer_class = GasCheckCreateUpdateSerializer
|
||||||
|
serializer_class = GasCheckSerializer
|
||||||
|
|
||||||
|
def create(self, request, *args, **kwargs):
|
||||||
|
obj = self.get_object()
|
||||||
|
if obj.opl.ticket:
|
||||||
|
raise ParseError('许可证已处理不可新增')
|
||||||
|
return super().create(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def destroy(self, request, *args, **kwargs):
|
||||||
|
obj = self.get_object()
|
||||||
|
if obj.opl.ticket:
|
||||||
|
raise ParseError('许可证已处理不可删除')
|
||||||
|
return super().destroy(request, *args, **kwargs)
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from apps.hrm.models import Certificate, Employee
|
from apps.hrm.models import Certificate, Employee
|
||||||
from apps.system.models import Dept, Dictionary, File, User
|
from apps.system.models import Dept, Dictionary, File, User
|
||||||
from apps.utils.models import CommonBModel, BaseModel
|
from apps.utils.models import CommonAModel, CommonBDModel, CommonBModel, BaseModel
|
||||||
|
from apps.wf.models import Ticket
|
||||||
|
|
||||||
|
|
||||||
# Create your models here.
|
# Create your models here.
|
||||||
|
@ -20,7 +21,6 @@ class Rparty(CommonBModel):
|
||||||
email = models.EmailField('邮箱', null=True, blank=True)
|
email = models.EmailField('邮箱', null=True, blank=True)
|
||||||
addresss = models.CharField('企业地址', max_length=200, null=True, blank=True)
|
addresss = models.CharField('企业地址', max_length=200, null=True, blank=True)
|
||||||
description = models.TextField('概述', null=True, blank=True)
|
description = models.TextField('概述', null=True, blank=True)
|
||||||
# belong_dept是归属部门
|
|
||||||
|
|
||||||
|
|
||||||
class RpartyFile(BaseModel):
|
class RpartyFile(BaseModel):
|
||||||
|
@ -33,15 +33,15 @@ class RpartyFile(BaseModel):
|
||||||
rparty = models.ForeignKey(Rparty, verbose_name='关联相关方', on_delete=models.CASCADE)
|
rparty = models.ForeignKey(Rparty, verbose_name='关联相关方', on_delete=models.CASCADE)
|
||||||
|
|
||||||
|
|
||||||
class Rproject(CommonBModel):
|
class Rpj(CommonBDModel):
|
||||||
"""
|
"""
|
||||||
相关方项目
|
相关方项目
|
||||||
"""
|
"""
|
||||||
RP_START = 10
|
RPJ_CREATE = 10
|
||||||
RP_APPROVAL = 20
|
RPJ_AUDIT = 20
|
||||||
RP_ENTER = 30
|
RPJ_ENTER = 30
|
||||||
RP_WORKING = 40
|
RPJ_WORKING = 40
|
||||||
RP_DONE = 50
|
RPJ_DONE = 50
|
||||||
RP_STATE_CHOICES = (
|
RP_STATE_CHOICES = (
|
||||||
(10, '创建中'),
|
(10, '创建中'),
|
||||||
(20, '审批中'),
|
(20, '审批中'),
|
||||||
|
@ -63,45 +63,72 @@ class Rproject(CommonBModel):
|
||||||
state = models.PositiveSmallIntegerField('状态', default=10)
|
state = models.PositiveSmallIntegerField('状态', default=10)
|
||||||
rparty = models.ForeignKey(Rparty, verbose_name='关联相关方', on_delete=models.CASCADE)
|
rparty = models.ForeignKey(Rparty, verbose_name='关联相关方', on_delete=models.CASCADE)
|
||||||
# belong_dept是业务部门可以带过来
|
# belong_dept是业务部门可以带过来
|
||||||
|
ticket = models.ForeignKey(Ticket, verbose_name='关联工单',
|
||||||
|
on_delete=models.CASCADE, null=True, blank=True)
|
||||||
|
|
||||||
|
|
||||||
class RprojectFile(BaseModel):
|
class RpjFile(BaseModel):
|
||||||
"""
|
"""
|
||||||
相关方项目文件库
|
相关方项目文件库
|
||||||
"""
|
"""
|
||||||
file_cate = models.ForeignKey(Dictionary, verbose_name='文件种类',
|
file_cate = models.ForeignKey(Dictionary, verbose_name='文件种类',
|
||||||
on_delete=models.CASCADE, null=True, blank=True)
|
on_delete=models.CASCADE, null=True, blank=True)
|
||||||
files = models.ManyToManyField(File, verbose_name='文件')
|
files = models.ManyToManyField(File, verbose_name='文件')
|
||||||
rproject = models.ForeignKey(Rproject, verbose_name='关联相关方项目', on_delete=models.CASCADE)
|
rpj = models.ForeignKey(Rpj, verbose_name='关联相关方项目', on_delete=models.CASCADE)
|
||||||
|
|
||||||
|
|
||||||
class Remployee(CommonBModel):
|
class Remployee(CommonAModel):
|
||||||
"""
|
"""
|
||||||
相关方成员
|
相关方成员
|
||||||
"""
|
"""
|
||||||
employee = models.OneToOneField(Employee, verbose_name='成员信息', on_delete=models.CASCADE)
|
employee = models.ForeignKey(Employee, verbose_name='成员信息',
|
||||||
rparty = models.ForeignKey(Rparty, verbose_name='所属相关方', on_delete=models.CASCADE)
|
|
||||||
rproject = models.ForeignKey(Rproject, verbose_name='当前所属相关方项目', on_delete=models.CASCADE,
|
|
||||||
null=True, blank=True)
|
|
||||||
|
|
||||||
|
|
||||||
class Rpeople(BaseModel):
|
|
||||||
"""
|
|
||||||
相关方项目人员
|
|
||||||
"""
|
|
||||||
rproject = models.ForeignKey(Rproject, verbose_name='关联项目', on_delete=models.CASCADE)
|
|
||||||
employee = models.ForeignKey(Employee, verbose_name='关联人员',
|
|
||||||
on_delete=models.CASCADE, null=True, blank=True)
|
on_delete=models.CASCADE, null=True, blank=True)
|
||||||
|
name = models.CharField('姓名', max_length=20)
|
||||||
|
phone = models.CharField('手机号', max_length=11)
|
||||||
|
photo = models.CharField('证件照', max_length=1000)
|
||||||
|
id_number = models.CharField('身份证号', max_length=100)
|
||||||
|
rparty = models.ForeignKey(Rparty, verbose_name='所属相关方', on_delete=models.CASCADE)
|
||||||
|
rpj = models.ForeignKey(Rpj, verbose_name='最近所属相关方项目', on_delete=models.CASCADE,
|
||||||
|
null=True, blank=True)
|
||||||
|
|
||||||
|
|
||||||
|
class Rcertificate(CommonAModel):
|
||||||
|
"""
|
||||||
|
相关方证书
|
||||||
|
"""
|
||||||
|
certificate = models.ForeignKey(Certificate, verbose_name='关联认可证书',
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
null=True, blank=True)
|
||||||
|
remployee = models.ForeignKey(Remployee, verbose_name='关联相关方成员',
|
||||||
|
on_delete=models.CASCADE)
|
||||||
|
name = models.CharField('证书名称', max_length=20)
|
||||||
|
number = models.CharField('证书编号', max_length=20, unique=True)
|
||||||
|
type = models.PositiveSmallIntegerField('证书类型', default=10, choices=Certificate.CERTIFICATE_TYPE_CHOICES)
|
||||||
|
issue_date = models.DateField('发证日期')
|
||||||
|
expiration_date = models.DateField('有效期')
|
||||||
|
review_date = models.DateField('下一次复审日期')
|
||||||
|
file = models.CharField('文件地址', max_length=1000, null=True, blank=True)
|
||||||
|
|
||||||
|
|
||||||
|
class Rpjmember(BaseModel):
|
||||||
|
"""
|
||||||
|
相关方项目成员
|
||||||
|
"""
|
||||||
|
rpj = models.ForeignKey(Rpj, verbose_name='关联项目', on_delete=models.CASCADE)
|
||||||
|
remployee = models.ForeignKey(Remployee, verbose_name='关联人员',
|
||||||
|
on_delete=models.CASCADE, null=True, blank=True)
|
||||||
|
duty = models.CharField('职责', max_length=20, null=True, blank=True)
|
||||||
is_manager = models.BooleanField('是否项目负责人', default=False)
|
is_manager = models.BooleanField('是否项目负责人', default=False)
|
||||||
|
rcertificates = models.ManyToManyField(Rcertificate, through='rpm.rpjcertificate')
|
||||||
|
|
||||||
|
|
||||||
class Rcertificate(BaseModel):
|
class Rpjcertificate(BaseModel):
|
||||||
"""
|
"""
|
||||||
相关方项目人员证书
|
相关方项目人员证书
|
||||||
"""
|
"""
|
||||||
rproject = models.ForeignKey(Rproject, verbose_name='关联项目', on_delete=models.CASCADE)
|
rpj_member = models.ForeignKey(Rpjmember, verbose_name='关联项目成员', on_delete=models.CASCADE)
|
||||||
employee = models.ForeignKey(Employee, verbose_name='关联人员',
|
rcertificate = models.ForeignKey(Rcertificate, verbose_name='关联相关方证书', on_delete=models.CASCADE,
|
||||||
on_delete=models.CASCADE, null=True, blank=True)
|
null=True, blank=True)
|
||||||
name = models.CharField('证书名称', max_length=20)
|
name = models.CharField('证书名称', max_length=20)
|
||||||
number = models.CharField('证书编号', max_length=20)
|
number = models.CharField('证书编号', max_length=20)
|
||||||
type = models.PositiveSmallIntegerField('证书类型', default=10, choices=Certificate.CERTIFICATE_TYPE_CHOICES)
|
type = models.PositiveSmallIntegerField('证书类型', default=10, choices=Certificate.CERTIFICATE_TYPE_CHOICES)
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
from apps.hrm.models import Employee
|
from apps.hrm.models import Certificate, Employee
|
||||||
from apps.hrm.serializers import phone_check
|
from apps.hrm.serializers import phone_check
|
||||||
from apps.rpm.models import Rparty, Rpeople, Rproject
|
from apps.rpm.models import Rcertificate, Remployee, Rparty, Rpjcertificate, Rpjmember, Rpj
|
||||||
from apps.system.models import Dept
|
from apps.system.models import Dept
|
||||||
from apps.utils.constants import EXCLUDE_FIELDS
|
from apps.utils.constants import EXCLUDE_FIELDS
|
||||||
from apps.utils.serializers import CustomModelSerializer
|
from apps.utils.serializers import CustomModelSerializer
|
||||||
from apps.system.serializers import DictSerializer, FileSerializer
|
from apps.system.serializers import DictSerializer, FileSerializer
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
from rest_framework.exceptions import ParseError
|
||||||
|
from django.db import transaction
|
||||||
|
|
||||||
|
|
||||||
class RpartyCreateUpdateSerializer(CustomModelSerializer):
|
class RpartyCreateUpdateSerializer(CustomModelSerializer):
|
||||||
|
@ -16,7 +18,7 @@ class RpartyCreateUpdateSerializer(CustomModelSerializer):
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
'belong_dept': {'required': True}
|
'belong_dept': {'required': True}
|
||||||
}
|
}
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
instance = super().create(validated_data)
|
instance = super().create(validated_data)
|
||||||
dept = Dept.objects.create(name=instance.name,
|
dept = Dept.objects.create(name=instance.name,
|
||||||
|
@ -56,41 +58,118 @@ class RpartyFileListSerializer(CustomModelSerializer):
|
||||||
files_ = FileSerializer(source='files', many=True, read_only=True)
|
files_ = FileSerializer(source='files', many=True, read_only=True)
|
||||||
|
|
||||||
|
|
||||||
class RprojectCreateUpdateSerializer(CustomModelSerializer):
|
class RpjCreateUpdateSerializer(CustomModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Rproject
|
model = Rpj
|
||||||
fields = ['name', 'contract_number', 'type', 'come_time', 'leave_time', 'belong_dept']
|
fields = ['name', 'contract_number', 'type', 'come_time', 'leave_time', 'belong_dept', 'rparty']
|
||||||
|
|
||||||
|
|
||||||
class RprojectListSerializer(CustomModelSerializer):
|
class RpjListSerializer(CustomModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Rproject
|
model = Rpj
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
|
|
||||||
|
|
||||||
class RemployeeCreateUpdateSerializer(CustomModelSerializer):
|
class RemployeeCreateSerializer(CustomModelSerializer):
|
||||||
phone = serializers.CharField(label="手机号", validators=[phone_check])
|
phone = serializers.CharField(label="手机号", validators=[phone_check])
|
||||||
rparty = serializers.PrimaryKeyRelatedField(queryset=Rparty.objects.all(), label='相关方ID', required=False)
|
rparty = serializers.PrimaryKeyRelatedField(queryset=Rparty.objects.all(), label='相关方ID', required=False)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Employee
|
model = Remployee
|
||||||
exclude = EXCLUDE_FIELDS + ['face_data',
|
fields = ['name', 'phone', 'photo', 'id_number']
|
||||||
'is_atwork', 'last_check_time',
|
# model = Employee
|
||||||
'not_work_remark', 'third_info', 'type']
|
# exclude = EXCLUDE_FIELDS + ['is_atwork', 'last_check_time',
|
||||||
extra_kwargs = {
|
# 'not_work_remark', 'third_info', 'type']
|
||||||
'phone': {'required': True},
|
# extra_kwargs = {
|
||||||
'number': {'required': True},
|
# 'phone': {'required': True},
|
||||||
'photo': {'required': True},
|
# 'number': {'required': True},
|
||||||
'id_number': {'required': True},
|
# 'photo': {'required': True},
|
||||||
}
|
# 'id_number': {'required': True},
|
||||||
|
# }
|
||||||
|
|
||||||
|
|
||||||
class RpeopleCreatesSerializer(CustomModelSerializer):
|
class RemployeeSerializer(CustomModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Remployee
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
|
||||||
|
class RcertificateCreateUpdateSerializer(CustomModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Rcertificate
|
||||||
|
exclude = EXCLUDE_FIELDS + ['certificate']
|
||||||
|
|
||||||
|
|
||||||
|
class RcertificateSerializer(CustomModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Rcertificate
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
|
||||||
|
class RpjmemberCreatesSerializer(CustomModelSerializer):
|
||||||
employees = serializers.PrimaryKeyRelatedField(
|
employees = serializers.PrimaryKeyRelatedField(
|
||||||
queryset=Employee.objects.filter(type='remployee'),
|
queryset=Employee.objects.filter(type='remployee'),
|
||||||
many=True, label='员工ID列表'
|
many=True, label='员工ID列表'
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Rpeople
|
model = Rpjmember
|
||||||
fields = ['employees', 'rproject']
|
fields = ['employees', 'Rpj']
|
||||||
|
|
||||||
|
|
||||||
|
class RpjmemberCreateSerializer(CustomModelSerializer):
|
||||||
|
rcertificates = serializers.PrimaryKeyRelatedField(label='证书ID', many=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Rpjmember
|
||||||
|
fields = ['employee', 'rpj', 'duty', 'is_manager']
|
||||||
|
|
||||||
|
def create(self, validated_data):
|
||||||
|
rpj = validated_data['rpj']
|
||||||
|
if rpj.state != Rpj.RPJ_CREATE:
|
||||||
|
raise ParseError('成员非创建状态不可新增')
|
||||||
|
rcertificates = validated_data.pop('rcertificates')
|
||||||
|
if Rpjmember.objects.filter(remployee=validated_data['remployee'], rpj=validated_data['rpj']).exists():
|
||||||
|
raise ParseError('该成员已选择')
|
||||||
|
with transaction.atomic():
|
||||||
|
ins = super().create(validated_data)
|
||||||
|
for x in rcertificates:
|
||||||
|
rpjc, _ = Rpjcertificate.objects.get_or_create(rpj_member=ins, rcertificate=x,
|
||||||
|
defaults={'rpj_member': ins, 'rcertificate': x})
|
||||||
|
for f in Rcertificate._meta.fields:
|
||||||
|
if f.name not in ['id']:
|
||||||
|
setattr(rpjc, f.name, getattr(x, f.name, None))
|
||||||
|
rpjc.save()
|
||||||
|
if ins.is_manager:
|
||||||
|
Rpjmember.objects.exclude(id__in=ins.id).update(is_manager=False)
|
||||||
|
return ins
|
||||||
|
|
||||||
|
|
||||||
|
class RpjmemberUpdateSerializer(CustomModelSerializer):
|
||||||
|
rcertificates = serializers.PrimaryKeyRelatedField(label='证书ID', many=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Rpjmember
|
||||||
|
fields = ['duty', 'is_manager', 'rcertificates']
|
||||||
|
|
||||||
|
def update(self, instance, validated_data):
|
||||||
|
rpj = validated_data['rpj']
|
||||||
|
if rpj.state != Rpj.RPJ_CREATE:
|
||||||
|
raise ParseError('成员非创建状态不可修改')
|
||||||
|
rcertificates = validated_data.pop('rcertificates')
|
||||||
|
with transaction.atomic():
|
||||||
|
instance = super().update(instance, validated_data)
|
||||||
|
for x in rcertificates:
|
||||||
|
rpjc, _ = Rpjcertificate.objects.get_or_create(rpj_member=instance, rcertificate=x,
|
||||||
|
defaults={'rpj_member': instance, 'rcertificate': x})
|
||||||
|
for f in Rcertificate._meta.fields:
|
||||||
|
if f.name not in ['id']:
|
||||||
|
setattr(rpjc, f.name, getattr(x, f.name, None))
|
||||||
|
rpjc.save()
|
||||||
|
return instance
|
||||||
|
|
||||||
|
|
||||||
|
class RpjmemberSerializer(CustomModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Rpjmember
|
||||||
|
fields = '__all__'
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
from apps.wf.models import Ticket, Transition
|
||||||
|
from apps.rpm.models import Rpj
|
||||||
|
|
||||||
|
|
||||||
|
def bind_opl(ticket: Ticket, transition: Transition, new_ticket_data: dict):
|
||||||
|
rpj = Rpj.objects.get(id=new_ticket_data['rpj'])
|
||||||
|
# ticket_data = ticket.ticket_data
|
||||||
|
# ticket_data.update({'level': opl.level})
|
||||||
|
# ticket.ticket_data = ticket_data
|
||||||
|
# ticket.save()
|
||||||
|
rpj.ticket = rpj
|
||||||
|
rpj.save()
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
# Create your tasks here
|
||||||
|
from __future__ import absolute_import, unicode_literals
|
||||||
|
from apps.hrm.models import Certificate, Employee
|
||||||
|
from apps.rpm.models import Rcertificate, Remployee, Rpj, Rpjcertificate, Rpjmember
|
||||||
|
from apps.utils.task import CustomTask
|
||||||
|
from celery import shared_task
|
||||||
|
|
||||||
|
|
||||||
|
@shared_task(base=CustomTask)
|
||||||
|
def rpj_audit_start(ticket_id):
|
||||||
|
rpj = Rpj.objects.get(ticket__id=ticket_id)
|
||||||
|
if rpj.state == Rpj.RPJ_CREATE:
|
||||||
|
rpj.state = Rpj.RPJ_AUDIT
|
||||||
|
rpj.save()
|
||||||
|
|
||||||
|
|
||||||
|
@shared_task(base=CustomTask)
|
||||||
|
def rpj_audit_end(ticket_id):
|
||||||
|
rpj = Rpj.objects.get(ticket__id=ticket_id)
|
||||||
|
if rpj.state == Rpj.RPJ_AUDIT:
|
||||||
|
rpj.state = Rpj.RPJ_ENTER
|
||||||
|
rpj.save()
|
||||||
|
# 更新企业相关方人员库和证书库
|
||||||
|
for i in Rpjmember.objects.filter(rpj=rpj):
|
||||||
|
remployee = i.remployee
|
||||||
|
ep = Employee.objects.filter(id_number=remployee.id_number).first()
|
||||||
|
if ep:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
ep = Employee()
|
||||||
|
ep.name = remployee.name
|
||||||
|
ep.phone = remployee.phone
|
||||||
|
ep.photo = remployee.photo
|
||||||
|
ep.id_number = remployee.id_number
|
||||||
|
ep.type = 'remployee'
|
||||||
|
ep.save()
|
||||||
|
remployee.employee = ep
|
||||||
|
remployee.save()
|
||||||
|
|
||||||
|
for i in Rpjcertificate.objects.filter(rpj_member__rpj=rpj):
|
||||||
|
ct = Certificate.objects.filter(number=i.number).first()
|
||||||
|
if ct:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
ct = Certificate()
|
||||||
|
ct.name = i.name
|
||||||
|
ct.number = i.number
|
||||||
|
ct.type = i.type
|
||||||
|
ct.issue_date = i.issue_date
|
||||||
|
ct.expiration_date = i.expiration_date
|
||||||
|
ct.review_date = i.review_date
|
||||||
|
ct.file = i.file
|
||||||
|
ct.employee = i.rpj_member.remployee.employee
|
||||||
|
ct.save()
|
|
@ -0,0 +1,18 @@
|
||||||
|
from apps.rpm.models import RpjFile
|
||||||
|
from apps.rpm.views import RpartyViewSet, RemployeeViewSet, RpartyFileViewSet, RpjViewSet, RpjmemberViewSet
|
||||||
|
from django.urls import path, include
|
||||||
|
from rest_framework.routers import DefaultRouter
|
||||||
|
|
||||||
|
API_BASE_URL = 'api/rpm/'
|
||||||
|
HTML_BASE_URL = 'rpm/'
|
||||||
|
|
||||||
|
router = DefaultRouter()
|
||||||
|
router.register('rparty', RpartyViewSet, basename='rparty')
|
||||||
|
router.register('remployee', RemployeeViewSet, basename='remployee')
|
||||||
|
router.register('rparty_file', RpartyFileViewSet, basename='rparty_file')
|
||||||
|
router.register('rpj', RpjViewSet, basename='rpj')
|
||||||
|
router.register('rpj_member', RpjmemberViewSet, basename='rpj_member')
|
||||||
|
router.register('rpj_file', RpjFile, basename='rpj_file')
|
||||||
|
urlpatterns = [
|
||||||
|
path(API_BASE_URL, include(router.urls)),
|
||||||
|
]
|
|
@ -1,11 +1,12 @@
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from apps.hrm.models import Certificate, Employee
|
from apps.hrm.models import Certificate, Employee
|
||||||
from apps.hrm.serializers import CertificateCreateUpdateSerializer, CertificateSerializer
|
from apps.hrm.serializers import CertificateCreateUpdateSerializer, CertificateSerializer, EmployeeSerializer
|
||||||
from apps.rpm.models import Remployee, Rparty, RpartyFile, Rpeople, Rproject
|
from apps.rpm.models import Rcertificate, Remployee, Rparty, RpartyFile, Rpjmember, Rpj
|
||||||
from apps.rpm.serializers import RemployeeCreateUpdateSerializer, RpartyAssignSerializer, RpartyCreateUpdateSerializer, RpartyFileListSerializer, RpartySerializer, RpeopleCreatesSerializer, RprojectCreateUpdateSerializer
|
from apps.rpm.serializers import RcertificateCreateUpdateSerializer, RcertificateSerializer, RemployeeCreateSerializer, RemployeeSerializer, RpartyAssignSerializer, RpartyCreateUpdateSerializer, RpartyFileListSerializer, RpartySerializer, RpjListSerializer, RpjmemberCreateSerializer, RpjmemberCreatesSerializer, RpjCreateUpdateSerializer, RpjmemberSerializer, RpjmemberUpdateSerializer
|
||||||
from apps.system.models import Post, User, UserPost
|
from apps.system.models import Post, User, UserPost
|
||||||
from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet
|
from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet
|
||||||
from rest_framework.mixins import CreateModelMixin, ListModelMixin
|
from rest_framework.mixins import CreateModelMixin, ListModelMixin, RetrieveModelMixin
|
||||||
|
from apps.utils.mixins import CustomDestoryModelMixin
|
||||||
from rest_framework.decorators import action
|
from rest_framework.decorators import action
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from django.contrib.auth.hashers import check_password, make_password
|
from django.contrib.auth.hashers import check_password, make_password
|
||||||
|
@ -59,59 +60,61 @@ class RpartyFileViewSet(ListModelMixin, CustomGenericViewSet):
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class RemployeeViewSet(CustomModelViewSet):
|
class RemployeeViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, CustomDestoryModelMixin,
|
||||||
queryset = Employee.objects.filter(type='remployee')
|
CustomGenericViewSet):
|
||||||
create_serializer_class = RemployeeCreateUpdateSerializer
|
queryset = Remployee.objects.all()
|
||||||
update_serializer_class = RemployeeCreateUpdateSerializer
|
create_serializer_class = RemployeeCreateSerializer
|
||||||
|
serializer_class = RemployeeSerializer
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
user = self.request.user
|
user = self.request.user
|
||||||
queryset = super().get_queryset()
|
queryset = super().get_queryset()
|
||||||
if user.type == 'remployee':
|
if user.type == 'remployee':
|
||||||
queryset = queryset.filter(belong_dept=user.belong_dept)
|
queryset = queryset.filter(rparty=self.request.user.belong_dept)
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
@transaction.atomic
|
# @transaction.atomic
|
||||||
def create(self, request, *args, **kwargs):
|
# def create(self, request, *args, **kwargs):
|
||||||
"""
|
# """
|
||||||
添加人员
|
# 添加人员
|
||||||
"""
|
# """
|
||||||
user = self.request.user
|
# user = self.request.user
|
||||||
serializer = self.get_serializer(data=request.data)
|
# serializer = self.get_serializer(data=request.data)
|
||||||
serializer.is_valid(raise_exception=True)
|
# serializer.is_valid(raise_exception=True)
|
||||||
vdata = serializer.validated_data
|
# vdata = serializer.validated_data
|
||||||
if user.type == 'remployee': # 如果是相关方账号
|
# if user.type == 'remployee': # 如果是相关方账号
|
||||||
ep = serializer.save()
|
# ep = serializer.save()
|
||||||
Remployee.objects.get_or_create(employee=ep, rparty=user.belong_dept.rparty,
|
# Remployee.objects.get_or_create(employee=ep, rparty=user.belong_dept.rparty,
|
||||||
defaults={
|
# defaults={
|
||||||
"employee": ep,
|
# "employee": ep,
|
||||||
"rparty": user.belong_dept.rparty
|
# "rparty": user.belong_dept.rparty
|
||||||
})
|
# })
|
||||||
else:
|
# else:
|
||||||
if 'rparty' not in vdata:
|
# if 'rparty' not in vdata:
|
||||||
raise ParseError('未指定相关方')
|
# raise ParseError('未指定相关方')
|
||||||
serializer.save()
|
# serializer.save()
|
||||||
return Response(serializer.data, status=201)
|
# return Response(serializer.data, status=201)
|
||||||
|
|
||||||
|
|
||||||
class Rcertificate(CustomModelViewSet):
|
class RcertificateViewSet(CustomModelViewSet):
|
||||||
queryset = Certificate.objects.filter(employee__type='remployee')
|
queryset = Rcertificate.objects.all()
|
||||||
create_serializer_class = CertificateCreateUpdateSerializer
|
create_serializer_class = RcertificateCreateUpdateSerializer
|
||||||
update_serializer_class = CertificateCreateUpdateSerializer
|
update_serializer_class = RcertificateCreateUpdateSerializer
|
||||||
serializer_class = CertificateSerializer
|
serializer_class = RcertificateSerializer
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
user = self.request.user
|
user = self.request.user
|
||||||
queryset = super().get_queryset()
|
queryset = super().get_queryset()
|
||||||
if user.type == 'remployee':
|
if user.type == 'remployee':
|
||||||
queryset = queryset.filter(belong_dept=user.belong_dept)
|
queryset = queryset.filter(remployee__rparty=user.belong_dept)
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class RprojectViewSet(CustomModelViewSet):
|
class RpjViewSet(CustomModelViewSet):
|
||||||
queryset = Rproject.objects.all()
|
queryset = Rpj.objects.all()
|
||||||
create_serializer_class = RprojectCreateUpdateSerializer
|
create_serializer_class = RpjCreateUpdateSerializer
|
||||||
update_serializer_class = RprojectCreateUpdateSerializer
|
update_serializer_class = RpjCreateUpdateSerializer
|
||||||
|
serializer_class = RpjListSerializer
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
user = self.request.user
|
user = self.request.user
|
||||||
|
@ -135,42 +138,26 @@ class RprojectViewSet(CustomModelViewSet):
|
||||||
|
|
||||||
def update(self, request, *args, **kwargs):
|
def update(self, request, *args, **kwargs):
|
||||||
obj = self.get_object()
|
obj = self.get_object()
|
||||||
if obj.state == Rproject.RP_START:
|
if obj.state != Rpj.RPJ_CREATE:
|
||||||
return super().update(request, *args, **kwargs)
|
|
||||||
raise ParseError('项目非创建状态不可更改')
|
|
||||||
|
|
||||||
|
|
||||||
class RpeopleViewSet(CustomGenericViewSet):
|
|
||||||
perms_map = {'get': '*'}
|
|
||||||
queryset = Rpeople.objects.all()
|
|
||||||
|
|
||||||
@action(methods=['post'], detail=False,
|
|
||||||
perms_map={'post': 'rproject:update'}, serializer_class=RpeopleCreatesSerializer)
|
|
||||||
@transaction.atomic
|
|
||||||
def creates(self, request, *args, **kwargs):
|
|
||||||
serializer = self.get_serializer(data=request.data)
|
|
||||||
serializer.is_valid(raise_exception=True)
|
|
||||||
vdata = serializer.validated_data
|
|
||||||
rp = vdata['rproject']
|
|
||||||
if rp.state != Rproject.RP_START:
|
|
||||||
raise ParseError('项目非创建状态不可更改')
|
raise ParseError('项目非创建状态不可更改')
|
||||||
for i in vdata['employees']:
|
return super().update(request, *args, **kwargs)
|
||||||
Rpeople.objects.create(employee=i, rproject=rp)
|
|
||||||
# 有证书的添加人员证书
|
|
||||||
|
|
||||||
return Response()
|
def destroy(self, request, *args, **kwargs):
|
||||||
|
|
||||||
@action(methods=['put'], detail=True,
|
|
||||||
perms_map={'put': 'rproject:update'}, serializer_class=serializers.Serializer)
|
|
||||||
@transaction.atomic
|
|
||||||
def make_manager(self, request):
|
|
||||||
"""
|
|
||||||
设为项目负责人
|
|
||||||
"""
|
|
||||||
obj = self.get_object()
|
obj = self.get_object()
|
||||||
if obj.rproject.state != Rproject.RP_START:
|
if obj.state != Rpj.RPJ_CREATE:
|
||||||
raise ParseError('项目非创建状态不可更改')
|
raise ParseError('项目非创建状态不可删除')
|
||||||
Rpeople.objects.filter(rproject=obj.rproject).update(is_manager=False)
|
return super().destroy(request, *args, **kwargs)
|
||||||
obj.is_manager = True
|
|
||||||
obj.save()
|
|
||||||
return Response()
|
class RpjmemberViewSet(CustomModelViewSet):
|
||||||
|
perms_map = {'get': '*', 'post': 'rpj:update', 'put': 'rpj:update', 'delete': 'rpj:update'}
|
||||||
|
queryset = Rpjmember.objects.all()
|
||||||
|
create_serializer_class = RpjmemberCreateSerializer
|
||||||
|
update_serializer_class = RpjmemberUpdateSerializer
|
||||||
|
serializer_class = RpjmemberSerializer
|
||||||
|
|
||||||
|
def destroy(self, request, *args, **kwargs):
|
||||||
|
obj = self.get_object()
|
||||||
|
if obj.rpj.state == Rpj.RPJ_CREATE:
|
||||||
|
raise ParseError('项目非创建状态不可删除')
|
||||||
|
return super().destroy(request, *args, **kwargs)
|
||||||
|
|
|
@ -11,6 +11,7 @@ from rest_framework.exceptions import ParseError
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from apps.third.tapis import dhapis
|
from apps.third.tapis import dhapis
|
||||||
from rest_framework.validators import UniqueValidator
|
from rest_framework.validators import UniqueValidator
|
||||||
|
from django.conf import settings
|
||||||
# from django_q.models import Task as QTask, Schedule as QSchedule
|
# from django_q.models import Task as QTask, Schedule as QSchedule
|
||||||
|
|
||||||
|
|
||||||
|
@ -242,7 +243,7 @@ class DeptCreateUpdateSerializer(CustomModelSerializer):
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
from apps.third.clients import dhClient
|
from apps.third.clients import dhClient
|
||||||
if dhClient:
|
if settings.DAHUA_ENABLED:
|
||||||
data = {
|
data = {
|
||||||
"parentId": 1,
|
"parentId": 1,
|
||||||
"name": validated_data['name'],
|
"name": validated_data['name'],
|
||||||
|
@ -257,7 +258,7 @@ class DeptCreateUpdateSerializer(CustomModelSerializer):
|
||||||
def update(self, instance, validated_data):
|
def update(self, instance, validated_data):
|
||||||
from apps.third.clients import dhClient
|
from apps.third.clients import dhClient
|
||||||
third_info = instance.third_info
|
third_info = instance.third_info
|
||||||
if dhClient and not third_info.get('dh_id', False):
|
if settings.DAHUA_ENABLED and not third_info.get('dh_id', False):
|
||||||
# 如果dh_id 不存在
|
# 如果dh_id 不存在
|
||||||
data = {
|
data = {
|
||||||
"parentId": 1,
|
"parentId": 1,
|
||||||
|
@ -269,7 +270,7 @@ class DeptCreateUpdateSerializer(CustomModelSerializer):
|
||||||
instance.third_info = third_info
|
instance.third_info = third_info
|
||||||
instance.save()
|
instance.save()
|
||||||
elif instance.name != validated_data.get('name', ''):
|
elif instance.name != validated_data.get('name', ''):
|
||||||
if dhClient and instance.third_info.get('dh_id', False):
|
if settings.DAHUA_ENABLED and instance.third_info.get('dh_id', False):
|
||||||
data = {
|
data = {
|
||||||
"id": instance.third_info['dh_id'],
|
"id": instance.third_info['dh_id'],
|
||||||
"parentId": 1,
|
"parentId": 1,
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
# Generated by Django 3.2.12 on 2022-06-17 07:19
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import django.utils.timezone
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('am', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='TDevice',
|
||||||
|
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='删除标记')),
|
||||||
|
('type', models.PositiveSmallIntegerField(choices=[(10, '定位基站'), (20, '定位信标'), (30, '定位标签'), (40, 'aoa引擎'), (50, '音响'), (60, '视频通道'), (70, '闸机通道'), (80, '面板机')], verbose_name='设备类型')),
|
||||||
|
('code', models.CharField(max_length=20, verbose_name='设备唯一标识')),
|
||||||
|
('location', models.JSONField(blank=True, default=dict, verbose_name='位置信息')),
|
||||||
|
('third_info', models.JSONField(blank=True, default=dict, verbose_name='三方信息')),
|
||||||
|
('area', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='am.area', verbose_name='所在区')),
|
||||||
|
('areas', models.ManyToManyField(related_name='tareas', to='am.Area', verbose_name='覆盖区')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
|
@ -18,15 +18,14 @@ class DhClient:
|
||||||
|
|
||||||
def __init__(self, client_id=settings.DAHUA_CLIENTID,
|
def __init__(self, client_id=settings.DAHUA_CLIENTID,
|
||||||
client_secret=settings.DAHUA_SECRET) -> None:
|
client_secret=settings.DAHUA_SECRET) -> None:
|
||||||
if not settings.DAHUA_ENABLED:
|
if settings.DAHUA_ENABLED:
|
||||||
return None
|
self.client_id = client_id
|
||||||
self.client_id = client_id
|
self.client_secret = client_secret
|
||||||
self.client_secret = client_secret
|
self.headers = {}
|
||||||
self.headers = {}
|
self.isGetingToken = False
|
||||||
self.isGetingToken = False
|
self.isRuning = True
|
||||||
self.isRuning = True
|
self.t = None # 线程
|
||||||
self.t = None # 线程
|
self.setup()
|
||||||
self.setup()
|
|
||||||
|
|
||||||
def _get_token_loop(self):
|
def _get_token_loop(self):
|
||||||
while self.isRuning:
|
while self.isRuning:
|
||||||
|
@ -70,7 +69,7 @@ class DhClient:
|
||||||
|
|
||||||
def request(self, url: str, method: str, params=dict(), json=dict(), timeout=10,
|
def request(self, url: str, method: str, params=dict(), json=dict(), timeout=10,
|
||||||
file_path_rela=None, raise_exception=True):
|
file_path_rela=None, raise_exception=True):
|
||||||
if self is None:
|
if not settings.DAHUA_ENABLED:
|
||||||
raise ParseError('大华对接未启用')
|
raise ParseError('大华对接未启用')
|
||||||
if self.isGetingToken:
|
if self.isGetingToken:
|
||||||
req_num = 0
|
req_num = 0
|
||||||
|
|
|
@ -152,7 +152,7 @@ class CommonBDModel(BaseModel):
|
||||||
'system.user', null=True, blank=True, on_delete=models.SET_NULL,
|
'system.user', null=True, blank=True, on_delete=models.SET_NULL,
|
||||||
verbose_name='最后编辑人', related_name='%(class)s_update_by')
|
verbose_name='最后编辑人', related_name='%(class)s_update_by')
|
||||||
belong_dept = models.ForeignKey(
|
belong_dept = models.ForeignKey(
|
||||||
'system.organzation', null=True, blank=True, on_delete=models.SET_NULL,
|
'system.dept', null=True, blank=True, on_delete=models.SET_NULL,
|
||||||
verbose_name='所属部门', related_name='%(class)s_belong_dept')
|
verbose_name='所属部门', related_name='%(class)s_belong_dept')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
|
@ -3,14 +3,14 @@ from django.core.cache import cache
|
||||||
from rest_framework.decorators import action
|
from rest_framework.decorators import action
|
||||||
from rest_framework.exceptions import ValidationError
|
from rest_framework.exceptions import ValidationError
|
||||||
from rest_framework.mixins import (CreateModelMixin, ListModelMixin,
|
from rest_framework.mixins import (CreateModelMixin, ListModelMixin,
|
||||||
RetrieveModelMixin, UpdateModelMixin)
|
RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin)
|
||||||
from rest_framework.permissions import IsAuthenticated
|
from rest_framework.permissions import IsAuthenticated
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.viewsets import GenericViewSet
|
from rest_framework.viewsets import GenericViewSet
|
||||||
|
|
||||||
from apps.system.models import DataFilter, Dept
|
from apps.system.models import DataFilter, Dept
|
||||||
from apps.utils.errors import PKS_ERROR
|
from apps.utils.errors import PKS_ERROR
|
||||||
from apps.utils.mixins import CustomDestoryModelMixin, MyLoggingMixin
|
from apps.utils.mixins import MyLoggingMixin
|
||||||
from apps.utils.permission import ALL_PERMS, RbacPermission, get_user_perms_map
|
from apps.utils.permission import ALL_PERMS, RbacPermission, get_user_perms_map
|
||||||
from apps.utils.queryset import get_child_queryset2
|
from apps.utils.queryset import get_child_queryset2
|
||||||
from apps.utils.serializers import PkSerializer
|
from apps.utils.serializers import PkSerializer
|
||||||
|
@ -101,7 +101,7 @@ class CustomGenericViewSet(MyLoggingMixin, GenericViewSet):
|
||||||
|
|
||||||
|
|
||||||
class CustomModelViewSet(CreateModelMixin, UpdateModelMixin, ListModelMixin,
|
class CustomModelViewSet(CreateModelMixin, UpdateModelMixin, ListModelMixin,
|
||||||
RetrieveModelMixin, CustomDestoryModelMixin, CustomGenericViewSet):
|
RetrieveModelMixin, DestroyModelMixin, CustomGenericViewSet):
|
||||||
"""
|
"""
|
||||||
增强的ModelViewSet
|
增强的ModelViewSet
|
||||||
"""
|
"""
|
||||||
|
@ -126,6 +126,12 @@ class CustomModelViewSet(CreateModelMixin, UpdateModelMixin, ListModelMixin,
|
||||||
pks = request_data.get('pks', None)
|
pks = request_data.get('pks', None)
|
||||||
if pks:
|
if pks:
|
||||||
self.get_queryset().filter(id__in=pks).delete(update_by=request.user)
|
self.get_queryset().filter(id__in=pks).delete(update_by=request.user)
|
||||||
return Response()
|
return Response(status=204)
|
||||||
else:
|
else:
|
||||||
raise ValidationError(**PKS_ERROR)
|
raise ValidationError(**PKS_ERROR)
|
||||||
|
|
||||||
|
def perform_destroy(self, instance):
|
||||||
|
if self.delete_soft:
|
||||||
|
instance.delete(update_by=self.request.user)
|
||||||
|
else:
|
||||||
|
instance.delete(soft=False)
|
||||||
|
|
|
@ -17,15 +17,14 @@ class XxClient:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, licence=settings.XX_LICENCE, username=settings.XX_USERNAME) -> None:
|
def __init__(self, licence=settings.XX_LICENCE, username=settings.XX_USERNAME) -> None:
|
||||||
if not settings.XX_ENABLED:
|
if settings.XX_ENABLED:
|
||||||
return None
|
self.licence = licence
|
||||||
self.licence = licence
|
self.username = username
|
||||||
self.username = username
|
self.isGetingToken = False
|
||||||
self.isGetingToken = False
|
self.isRuning = True
|
||||||
self.isRuning = True
|
self.token = ''
|
||||||
self.token = ''
|
self.t = None
|
||||||
self.t = None
|
self.setup()
|
||||||
self.setup()
|
|
||||||
|
|
||||||
def _get_token_loop(self):
|
def _get_token_loop(self):
|
||||||
while self.isRuning:
|
while self.isRuning:
|
||||||
|
@ -58,10 +57,10 @@ class XxClient:
|
||||||
自定义销毁
|
自定义销毁
|
||||||
"""
|
"""
|
||||||
self.isRuning = False
|
self.isRuning = False
|
||||||
self.t.join()
|
# self.t.join()
|
||||||
|
|
||||||
def request(self, url: str, method: str = 'post', params=dict(), json=dict(), timeout=4, raise_exception=True):
|
def request(self, url: str, method: str = 'post', params=dict(), json=dict(), timeout=4, raise_exception=True):
|
||||||
if self is None:
|
if not settings.XX_ENABLED:
|
||||||
raise ParseError('寻息对接未启用')
|
raise ParseError('寻息对接未启用')
|
||||||
params['accessToken'] = self.token
|
params['accessToken'] = self.token
|
||||||
json['username'] = self.username
|
json['username'] = self.username
|
||||||
|
|
|
@ -15,17 +15,44 @@ class Visit(CommonBModel):
|
||||||
(30, '面试'),
|
(30, '面试'),
|
||||||
(40, '开会')
|
(40, '开会')
|
||||||
)
|
)
|
||||||
|
V_CREATE = 10
|
||||||
|
V_AUDIT = 20
|
||||||
|
V_ENTER = 30
|
||||||
|
V_WORKING = 40
|
||||||
|
V_DONE = 50
|
||||||
|
V_STATE_CHOICES = (
|
||||||
|
(10, '创建中'),
|
||||||
|
(20, '审批中'),
|
||||||
|
(30, '待入厂'),
|
||||||
|
(40, '进行中'),
|
||||||
|
(50, '已完成')
|
||||||
|
)
|
||||||
purpose = models.PositiveSmallIntegerField('来访事由')
|
purpose = models.PositiveSmallIntegerField('来访事由')
|
||||||
description = models.CharField('来访详述', max_length=200)
|
state = models.PositiveSmallIntegerField(choices=V_STATE_CHOICES, default=10)
|
||||||
|
name = models.CharField('来访概述', max_length=50)
|
||||||
|
description = models.TextField('来访详述', null=True, blank=True)
|
||||||
visit_time = models.DateTimeField('来访时间')
|
visit_time = models.DateTimeField('来访时间')
|
||||||
leave_time = models.DateTimeField('离开时间')
|
leave_time = models.DateTimeField('离开时间')
|
||||||
receptionist = models.ForeignKey(User, verbose_name='接待人', on_delete=models.CASCADE)
|
receptionist = models.ForeignKey(User, verbose_name='接待人', on_delete=models.CASCADE)
|
||||||
|
|
||||||
|
|
||||||
class VisitPeople(BaseModel):
|
class Visitor(CommonAModel):
|
||||||
|
"""
|
||||||
|
访客信息
|
||||||
|
"""
|
||||||
|
employee = models.ForeignKey(Employee, verbose_name='成员信息', on_delete=models.CASCADE, null=True, blank=True)
|
||||||
|
name = models.CharField('姓名', max_length=20)
|
||||||
|
phone = models.CharField('手机号', max_length=11)
|
||||||
|
photo = models.CharField('证件照', max_length=1000)
|
||||||
|
id_number = models.CharField('身份证号', max_length=100)
|
||||||
|
visit = models.ForeignKey(Visit, verbose_name='最近所属访问项目', on_delete=models.CASCADE,
|
||||||
|
null=True, blank=True)
|
||||||
|
|
||||||
|
|
||||||
|
class Vpeople(BaseModel):
|
||||||
"""
|
"""
|
||||||
访客项目人员
|
访客项目人员
|
||||||
"""
|
"""
|
||||||
visit = models.ForeignKey(Visit, verbose_name='关联访问项目', on_delete=models.CASCADE)
|
visit = models.ForeignKey(Visit, verbose_name='关联访问项目', on_delete=models.CASCADE)
|
||||||
visitor = models.ForeignKey(Employee, verbose_name='访客', on_delete=models.CASCADE)
|
visitor = models.ForeignKey(Visitor, verbose_name='访客', on_delete=models.CASCADE)
|
||||||
is_manager = models.BooleanField('是否主访人', default=False)
|
is_main = models.BooleanField('是否主访人', default=False)
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
from apps.hrm.models import Employee
|
||||||
|
from apps.utils.constants import EXCLUDE_FIELDS
|
||||||
|
from apps.utils.serializers import CustomModelSerializer
|
||||||
|
from apps.vm.models import Visit, Visitor, Vpeople
|
||||||
|
from apps.hrm.serializers import phone_check
|
||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
|
||||||
|
class VisitCreateUpdateSerializer(CustomModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Visit
|
||||||
|
fields = ['purpose', 'name', 'description', 'visit_time', 'leave_time', 'receptionist']
|
||||||
|
|
||||||
|
|
||||||
|
class VisitSerializer(CustomModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Visit
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
|
||||||
|
class VisitorCreateSerializer(CustomModelSerializer):
|
||||||
|
phone = serializers.CharField(label="手机号", validators=[phone_check])
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Visitor
|
||||||
|
exclude = ['name', 'phone', 'photo', 'id_number']
|
||||||
|
|
||||||
|
|
||||||
|
class VisitorSerializer(CustomModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Visitor
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
|
||||||
|
class VpeopleCreateSerializer(CustomModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Vpeople
|
||||||
|
fields = ['visitor', 'visitor', 'is_main']
|
||||||
|
|
||||||
|
def create(self, validated_data):
|
||||||
|
ins = super().create(validated_data)
|
||||||
|
if ins.is_main:
|
||||||
|
Vpeople.objects.filter(visit=validated_data['visit']).exclude(id__in=ins.id).update(is_main=False)
|
||||||
|
return ins
|
||||||
|
|
||||||
|
|
||||||
|
class VpeopleSerializer(CustomModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Vpeople
|
||||||
|
fields = '__all__'
|
|
@ -0,0 +1,30 @@
|
||||||
|
# Create your tasks here
|
||||||
|
from __future__ import absolute_import, unicode_literals
|
||||||
|
from apps.hrm.models import Employee
|
||||||
|
from apps.utils.task import CustomTask
|
||||||
|
from apps.vm.models import Visit, Vpeople
|
||||||
|
from celery import shared_task
|
||||||
|
|
||||||
|
|
||||||
|
@shared_task(base=CustomTask)
|
||||||
|
def visit_audit_end(ticket_id):
|
||||||
|
visit = Visit.objects.get(ticket__id=ticket_id)
|
||||||
|
if visit.state == Visit.V_AUDIT:
|
||||||
|
visit.state = Visit.V_ENTER
|
||||||
|
visit.save()
|
||||||
|
# 更新企业访客人员库
|
||||||
|
for i in Vpeople.objects.filter(visit=visit):
|
||||||
|
visitor = i.visitor
|
||||||
|
ep = Employee.objects.filter(id_number=visitor.id_number).first()
|
||||||
|
if ep:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
ep = Employee()
|
||||||
|
ep.name = visitor.name
|
||||||
|
ep.phone = visitor.phone
|
||||||
|
ep.photo = visitor.photo
|
||||||
|
ep.id_number = visitor.id_number
|
||||||
|
ep.type = 'visitor'
|
||||||
|
ep.save()
|
||||||
|
visitor.employee = ep
|
||||||
|
visitor.save()
|
|
@ -1,3 +1,62 @@
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
|
from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet, GenericViewSet
|
||||||
|
from apps.vm.models import Visit, Visitor, Vpeople
|
||||||
|
from apps.vm.serializers import VisitCreateUpdateSerializer, VisitSerializer, VisitorCreateSerializer, VisitorSerializer, VpeopleCreateSerializer, VpeopleSerializer
|
||||||
|
from rest_framework.decorators import action
|
||||||
|
from rest_framework.response import Response
|
||||||
|
from rest_framework.exceptions import ParseError
|
||||||
|
from rest_framework.mixins import ListModelMixin, RetrieveModelMixin, CreateModelMixin, DestroyModelMixin
|
||||||
|
from apps.utils.mixins import CustomDestoryModelMixin
|
||||||
# Create your views here.
|
# Create your views here.
|
||||||
|
|
||||||
|
|
||||||
|
class VisitViewSet(CustomModelViewSet):
|
||||||
|
queryset = Visit.objects.all()
|
||||||
|
create_serializer_class = VisitCreateUpdateSerializer
|
||||||
|
update_serializer_class = VisitCreateUpdateSerializer
|
||||||
|
serializer_class = VisitSerializer
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
user = self.request.user
|
||||||
|
queryset = super().get_queryset()
|
||||||
|
if user.type == 'visitor':
|
||||||
|
queryset = queryset.filter(create_by=user)
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
def update(self, request, *args, **kwargs):
|
||||||
|
obj = self.get_object()
|
||||||
|
if obj.state != Visit.V_CREATE:
|
||||||
|
raise ParseError('项目非创建状态不可更改')
|
||||||
|
return super().update(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def destroy(self, request, *args, **kwargs):
|
||||||
|
obj = self.get_object()
|
||||||
|
if obj.state != Visit.V_CREATE:
|
||||||
|
raise ParseError('项目非创建状态不可删除')
|
||||||
|
return super().destroy(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class VisitorViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, CustomDestoryModelMixin,
|
||||||
|
CustomGenericViewSet):
|
||||||
|
queryset = Visitor.objects.all()
|
||||||
|
create_serializer_class = VisitorCreateSerializer
|
||||||
|
serializer_class = VisitorSerializer
|
||||||
|
|
||||||
|
|
||||||
|
class VpeopleView(ListModelMixin, RetrieveModelMixin, CreateModelMixin, DestroyModelMixin, CustomGenericViewSet):
|
||||||
|
perms_map = {'get': '*', 'post': 'visit:update', 'put': 'visit:update', 'delete': 'visit:update'}
|
||||||
|
queryset = Vpeople.objects.all()
|
||||||
|
create_serializer_class = VpeopleCreateSerializer
|
||||||
|
serializer_class = VpeopleSerializer
|
||||||
|
|
||||||
|
def create(self, request, *args, **kwargs):
|
||||||
|
obj = self.get_object()
|
||||||
|
if obj.visit.state != Visit.V_CREATE:
|
||||||
|
raise ParseError('项目非创建状态不可新增成员')
|
||||||
|
return super().create(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def destroy(self, request, *args, **kwargs):
|
||||||
|
obj = self.get_object()
|
||||||
|
if obj.visit.state != Visit.V_CREATE:
|
||||||
|
raise ParseError('项目非创建状态不可新增成员')
|
||||||
|
return super().destroy(request, *args, **kwargs)
|
||||||
|
|
|
@ -12,6 +12,7 @@ pillow==9.0.1
|
||||||
opencv-python==4.5.5.62
|
opencv-python==4.5.5.62
|
||||||
daphne==3.0.2
|
daphne==3.0.2
|
||||||
redis==4.1.4
|
redis==4.1.4
|
||||||
|
django-redis==5.2.0
|
||||||
user-agents==2.2.0
|
user-agents==2.2.0
|
||||||
daphne==3.0.2
|
daphne==3.0.2
|
||||||
channels==3.0.4
|
channels==3.0.4
|
||||||
|
|
|
@ -11,6 +11,7 @@ https://docs.djangoproject.com/en/3.0/ref/settings/
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
import json
|
||||||
import os
|
import os
|
||||||
from . import conf
|
from . import conf
|
||||||
import logging
|
import logging
|
||||||
|
@ -54,6 +55,7 @@ INSTALLED_APPS = [
|
||||||
'apps.auth1',
|
'apps.auth1',
|
||||||
'apps.monitor',
|
'apps.monitor',
|
||||||
'apps.wf',
|
'apps.wf',
|
||||||
|
'apps.ecm',
|
||||||
'apps.hrm',
|
'apps.hrm',
|
||||||
'apps.am',
|
'apps.am',
|
||||||
'apps.vm',
|
'apps.vm',
|
||||||
|
@ -194,8 +196,8 @@ REST_FRAMEWORK = {
|
||||||
# 'UNAUTHENTICATED_TOKEN': None,
|
# 'UNAUTHENTICATED_TOKEN': None,
|
||||||
'EXCEPTION_HANDLER': 'apps.utils.exceptions.custom_exception_hander',
|
'EXCEPTION_HANDLER': 'apps.utils.exceptions.custom_exception_hander',
|
||||||
'DEFAULT_THROTTLE_RATES': {
|
'DEFAULT_THROTTLE_RATES': {
|
||||||
'anon': '1/second',
|
'anon': '100/second',
|
||||||
'user': '2/second'
|
'user': '200/second'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
# simplejwt配置
|
# simplejwt配置
|
||||||
|
@ -214,16 +216,15 @@ AUTHENTICATION_BACKENDS = (
|
||||||
)
|
)
|
||||||
|
|
||||||
# 缓存配置,有需要可更改为redis
|
# 缓存配置,有需要可更改为redis
|
||||||
# CACHES = {
|
CACHES = {
|
||||||
# "default": {
|
"default": {
|
||||||
# "BACKEND": "django_redis.cache.RedisCache",
|
"BACKEND": "django_redis.cache.RedisCache",
|
||||||
# "LOCATION": "redis://redis:6379/1",
|
"LOCATION": "redis://localhost:6379/2",
|
||||||
# "OPTIONS": {
|
"OPTIONS": {
|
||||||
# "CLIENT_CLASS": "django_redis.client.DefaultClient",
|
"CLIENT_CLASS": "django_redis.client.DefaultClient",
|
||||||
# "PICKLE_VERSION": -1
|
}
|
||||||
# }
|
}
|
||||||
# }
|
}
|
||||||
# }
|
|
||||||
|
|
||||||
# celery配置,celery正常运行必须安装redis
|
# celery配置,celery正常运行必须安装redis
|
||||||
CELERY_BROKER_URL = "redis://localhost:6379/2" # 任务存储
|
CELERY_BROKER_URL = "redis://localhost:6379/2" # 任务存储
|
||||||
|
@ -257,8 +258,8 @@ CELERY_TASK_TRACK_STARTED = True
|
||||||
|
|
||||||
# swagger配置
|
# swagger配置
|
||||||
SWAGGER_SETTINGS = {
|
SWAGGER_SETTINGS = {
|
||||||
'LOGIN_URL': '/django/login/',
|
'LOGIN_URL': '/django/admin/login/',
|
||||||
'LOGOUT_URL': '/django/logout/',
|
'LOGOUT_URL': '/django/admin/logout/',
|
||||||
}
|
}
|
||||||
|
|
||||||
# 日志配置
|
# 日志配置
|
||||||
|
|
|
@ -36,8 +36,8 @@ schema_view = get_schema_view(
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
# django后台
|
# django后台
|
||||||
path('django/doc/', include('django.contrib.admindocs.urls')),
|
path('django/admin/doc/', include('django.contrib.admindocs.urls')),
|
||||||
path('django/', admin.site.urls),
|
path('django/admin/', admin.site.urls),
|
||||||
path('django/api-auth/', include('rest_framework.urls')),
|
path('django/api-auth/', include('rest_framework.urls')),
|
||||||
|
|
||||||
# api
|
# api
|
||||||
|
|
Loading…
Reference in New Issue