diff --git a/apps/enm/migrations/0050_mpoint_is_rep_ep0_running_state.py b/apps/enm/migrations/0050_mpoint_is_rep_ep0_running_state.py new file mode 100644 index 00000000..bdfd566d --- /dev/null +++ b/apps/enm/migrations/0050_mpoint_is_rep_ep0_running_state.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.12 on 2024-12-11 09:36 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('enm', '0049_enstat_ammonia_consume'), + ] + + operations = [ + migrations.AddField( + model_name='mpoint', + name='is_rep_ep0_running_state', + field=models.BooleanField(default=False, verbose_name='是否表示所属设备运行状态'), + ), + ] diff --git a/apps/enm/models.py b/apps/enm/models.py index e9d1e14c..7a9e5db9 100644 --- a/apps/enm/models.py +++ b/apps/enm/models.py @@ -41,6 +41,7 @@ class Mpoint(CommonBModel): third_info = models.JSONField("第三方信息", default=dict, blank=True) # {"from": "king", "n": "某名称","d": "某描述或备注","g": "某组", "t": "某类型", "id": 5001, "o": "其他信息"} enp_field = models.CharField("关联enp采集字段", max_length=50, null=True, blank=True) + is_rep_ep0_running_state = models.BooleanField("是否表示所属设备运行状态", default=False) is_rep_ep_running_state = models.BooleanField("是否表示所监测设备运行状态", default=False) ep_monitored = models.ForeignKey("em.equipment", verbose_name="所监测设备", related_name="mp_ep_monitored", on_delete=models.SET_NULL, null=True, blank=True) ep_rs_val = models.FloatField("状态量基准值", null=True, blank=True) diff --git a/apps/enm/services.py b/apps/enm/services.py index ebf74492..1a82c00a 100644 --- a/apps/enm/services.py +++ b/apps/enm/services.py @@ -223,9 +223,10 @@ class MpointCache: # 下面开始更新设备信号 ep_belong_id = current_cache_val.get("ep_belong") ep_monitored_id = current_cache_val.get("ep_monitored") - if ep_monitored_id and mpoint_is_rep_ep_running_state and ep_belong_id != ep_monitored_id: + mpoint_is_rep_ep0_running_state = current_cache_val.get("is_rep_ep0_running_state", False) + if ep_monitored_id and mpoint_is_rep_ep_running_state: set_eq_rs(ep_monitored_id, last_timex, last_mrs) - if ep_belong_id: + if ep_belong_id and mpoint_is_rep_ep0_running_state and ep_belong_id != ep_monitored_id: set_eq_rs(ep_belong_id, last_timex, Equipment.RUNING) mf_code = current_cache_val.get("mpoint_affect") diff --git a/apps/mtm/filters.py b/apps/mtm/filters.py index c36c4568..7724936d 100644 --- a/apps/mtm/filters.py +++ b/apps/mtm/filters.py @@ -11,6 +11,7 @@ class MaterialFilter(filters.FilterSet): fields = { "id": ["exact", "in"], "cate": ["exact", "in"], + "code": ["exact", "in", "isnull"], "type": ["exact", "in"], "is_hidden": ["exact"], "is_assemb": ["exact"], diff --git a/apps/mtm/migrations/0044_mgroup_code.py b/apps/mtm/migrations/0044_mgroup_code.py new file mode 100644 index 00000000..152f3680 --- /dev/null +++ b/apps/mtm/migrations/0044_mgroup_code.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.12 on 2024-12-12 03:07 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('mtm', '0043_srule'), + ] + + operations = [ + migrations.AddField( + model_name='mgroup', + name='code', + field=models.CharField(blank=True, max_length=50, null=True, verbose_name='标识'), + ), + ] diff --git a/apps/mtm/migrations/0045_process_type.py b/apps/mtm/migrations/0045_process_type.py new file mode 100644 index 00000000..baad4f37 --- /dev/null +++ b/apps/mtm/migrations/0045_process_type.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.12 on 2024-12-12 07:16 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('mtm', '0044_mgroup_code'), + ] + + operations = [ + migrations.AddField( + model_name='process', + name='type', + field=models.PositiveSmallIntegerField(choices=[(10, '生产工序'), (20, '检验工序')], default=10, verbose_name='工序类型'), + ), + ] diff --git a/apps/mtm/models.py b/apps/mtm/models.py index ac76a2b6..83624892 100644 --- a/apps/mtm/models.py +++ b/apps/mtm/models.py @@ -8,8 +8,10 @@ class Process(CommonBModel): """ 工序 """ - + PRO_PROD = 10 + RPO_TEST = 20 name = models.CharField('工序名称', max_length=100) + type = models.PositiveSmallIntegerField("工序类型", default=PRO_PROD, choices=((PRO_PROD, '生产工序'), (RPO_TEST, '检验工序'))) cate = models.CharField('大类', max_length=10, default='') sort = models.PositiveSmallIntegerField('排序', default=1) instruction = models.ForeignKey( @@ -118,6 +120,7 @@ class Mgroup(CommonBModel): """ name = models.CharField('名称', max_length=50) + code = models.CharField('标识', max_length=50, null=True, blank=True) cate = models.CharField( '分类', max_length=50, default='section', help_text='section/other') # section是工段 shift_rule = models.CharField('班次规则', max_length=10, default='默认') diff --git a/apps/mtm/serializers.py b/apps/mtm/serializers.py index 167a176f..6f7a72c9 100644 --- a/apps/mtm/serializers.py +++ b/apps/mtm/serializers.py @@ -59,6 +59,7 @@ class MgroupSerializer(CustomModelSerializer): belong_dept_name = serializers.CharField( source='belong_dept.name', read_only=True) process_name = serializers.CharField(source='process.name', read_only=True) + process_type = serializers.CharField(source='process.type', read_only=True) process_cate = serializers.CharField(source='process.cate', read_only=True) class Meta: diff --git a/apps/mtm/views.py b/apps/mtm/views.py index cdc3dad0..e878d779 100644 --- a/apps/mtm/views.py +++ b/apps/mtm/views.py @@ -102,7 +102,8 @@ class MgroupViewSet(CustomModelViewSet): "process": ["exact"], "cate": ["exact"], "belong_dept__name": ["exact", "contains"], - "name": ["exact", "contains"] + "name": ["exact", "contains"], + "code": ["exact", "in", "isnull"] } search_fields = ['name'] ordering = ['sort', 'create_time'] diff --git a/apps/qm/migrations/0027_ftestwork_ticket.py b/apps/qm/migrations/0027_ftestwork_ticket.py new file mode 100644 index 00000000..00062147 --- /dev/null +++ b/apps/qm/migrations/0027_ftestwork_ticket.py @@ -0,0 +1,20 @@ +# Generated by Django 3.2.12 on 2024-12-12 06:39 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('wf', '0002_alter_state_filter_dept'), + ('qm', '0026_auto_20241121_1044'), + ] + + operations = [ + migrations.AddField( + model_name='ftestwork', + name='ticket', + field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='ftestwork_ticket', to='wf.ticket', verbose_name='关联工单'), + ), + ] diff --git a/apps/qm/models.py b/apps/qm/models.py index 823e5c51..7be55065 100644 --- a/apps/qm/models.py +++ b/apps/qm/models.py @@ -152,6 +152,9 @@ class FtestWork(CommonBDModel): submit_user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='提交人', null=True, blank=True) note = models.TextField('备注', null=True, blank=True) equipment = models.ForeignKey(Equipment, verbose_name='所属检验设备', on_delete=models.SET_NULL, null=True, blank=True) + ticket = models.ForeignKey('wf.ticket', verbose_name='关联工单', + on_delete=models.SET_NULL, related_name='ftestwork_ticket', null=True, blank=True, + db_constraint=False) @classmethod def count_fields(cls): diff --git a/apps/qm/services.py b/apps/qm/services.py index 18f6426b..53855959 100644 --- a/apps/qm/services.py +++ b/apps/qm/services.py @@ -6,6 +6,18 @@ from django.utils import timezone from apps.wf.models import Ticket from apps.qm.models import NotOkOption +def ftestwork_submit_validate(ins: FtestWork): + wm:WMaterial = ins.wm + if ins.need_update_wm: + if wm.state == WMaterial.WM_TEST: + xcount = wm.count - ins.count + if xcount < 0: + raise ParseError("超过待检数量") + else: + xcount = wm.count - ins.count_notok + if xcount < 0: + raise ParseError("不合格数不可大于批次数量") + def ftestwork_submit(ins:FtestWork, user: User): wm:WMaterial = ins.wm @@ -83,4 +95,22 @@ def ftestwork_submit(ins:FtestWork, user: User): ins.save() def bind_ftestwork(ticket: Ticket, transition, new_ticket_data: dict): - pass \ No newline at end of file + ins = FtestWork.objects.get(id=new_ticket_data['t_id']) + if ins.submit_time is not None: + raise ParseError('该检验工作不可提交审批') + ftestwork_submit_validate(ins) + ticket_data = ticket.ticket_data + ticket_data.update({ + 't_model': 'ftestwork', + 't_id': ins.id, + }) + ticket.ticket_data = ticket_data + ticket.create_by = ins.create_by + ticket.save() + if ins.ticket is None: + ins.ticket = ticket + ins.save() + +def ftestwork_audit_end(ticket: Ticket): + ins = FtestWork.objects.get(id=ticket.ticket_data['t_id']) + ftestwork_submit(ins, ticket.create_by) \ No newline at end of file diff --git a/apps/system/migrations/0006_auto_20241213_1249.py b/apps/system/migrations/0006_auto_20241213_1249.py new file mode 100644 index 00000000..6f5727ec --- /dev/null +++ b/apps/system/migrations/0006_auto_20241213_1249.py @@ -0,0 +1,43 @@ +# Generated by Django 3.2.12 on 2024-12-13 04:49 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('system', '0005_alter_permission_type'), + ] + + operations = [ + migrations.AddField( + model_name='permission', + name='component', + field=models.CharField(blank=True, max_length=100, null=True, verbose_name='组件'), + ), + migrations.AddField( + model_name='permission', + name='icon', + field=models.CharField(blank=True, max_length=30, null=True, verbose_name='图标'), + ), + migrations.AddField( + model_name='permission', + name='is_fullpage', + field=models.BooleanField(default=False, verbose_name='是否全屏'), + ), + migrations.AddField( + model_name='permission', + name='is_hidden', + field=models.BooleanField(default=False, verbose_name='是否隐藏'), + ), + migrations.AddField( + model_name='permission', + name='path', + field=models.CharField(blank=True, max_length=100, null=True, verbose_name='路由'), + ), + migrations.AddField( + model_name='permission', + name='route_name', + field=models.CharField(blank=True, max_length=30, null=True, verbose_name='路由名称'), + ), + ] diff --git a/apps/system/models.py b/apps/system/models.py index 516704c0..53658e3b 100755 --- a/apps/system/models.py +++ b/apps/system/models.py @@ -30,6 +30,13 @@ class Permission(ParentModel, BaseModel): '类型', choices=menu_type_choices, default=30) sort = models.PositiveSmallIntegerField('排序标记', default=1) codes = models.JSONField('权限标识', default=list, null=True, blank=True) + + route_name = models.CharField('路由名称', max_length=30, null=True, blank=True) + icon = models.CharField('图标', max_length=30, null=True, blank=True) + path = models.CharField('路由', max_length=100, null=True, blank=True) + component = models.CharField('组件', max_length=100, null=True, blank=True) + is_hidden = models.BooleanField('是否隐藏', default=False) + is_fullpage = models.BooleanField('是否全屏', default=False) def __str__(self): return self.name diff --git a/apps/system/serializers.py b/apps/system/serializers.py index 3050437c..d95dd3f5 100755 --- a/apps/system/serializers.py +++ b/apps/system/serializers.py @@ -17,6 +17,7 @@ from apps.third.tapis import dhapis from rest_framework.validators import UniqueValidator from django.conf import settings from django.db.models import Q +from apps.utils.permission import get_user_perms_map # from django_q.models import Task as QTask, Schedule as QSchedule @@ -334,6 +335,23 @@ class UserUpdateSerializer(CustomModelSerializer): return super().update(instance, validated_data) +class UserFullInfoSerializer(CustomModelSerializer): + """ + 用户信息序列化 + """ + perms = serializers.SerializerMethodField() + belong_dept_name = serializers.CharField(source="belong_dept.name", read_only=True) + post_name = serializers.CharField(source="post.name", read_only=True) + + class Meta: + model = User + fields = ['id', 'username', 'type', 'name', 'avatar', 'belong_dept', + 'belong_dept_name', 'post', 'post_name', 'perms', + 'is_superuser', 'wxmp_openid', 'wx_openid'] + + def get_perms(self, obj): + return get_user_perms_map(obj, update_cache=True) + class UserCreateSerializer(CustomModelSerializer): """ 创建用户序列化 diff --git a/apps/system/views.py b/apps/system/views.py index ce92f414..ac1e6a7d 100755 --- a/apps/system/views.py +++ b/apps/system/views.py @@ -22,7 +22,7 @@ from apps.system.filters import DeptFilterSet, UserFilterSet # from django_q.models import Task as QTask, Schedule as QSchedule from apps.utils.mixins import (CustomCreateModelMixin, MyLoggingMixin) from django.conf import settings -from apps.utils.permission import ALL_PERMS, get_user_perms_map +from apps.utils.permission import ALL_PERMS from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet from server.celery import app as celery_app from .models import (Dept, Dictionary, DictType, File, Permission, Post, PostRole, Role, User, @@ -35,7 +35,8 @@ from .serializers import (ApkSerializer, DeptCreateUpdateSerializer, DeptSeriali PTaskSerializer, PTaskCreateUpdateSerializer, PTaskResultSerializer, RoleCreateUpdateSerializer, RoleSerializer, TaskRunSerializer, UserCreateSerializer, UserListSerializer, UserPostCreateSerializer, - UserPostSerializer, UserUpdateSerializer, MyScheduleCreateSerializer, MyScheduleSerializer) + UserPostSerializer, UserUpdateSerializer, UserFullInfoSerializer, + MyScheduleCreateSerializer, MyScheduleSerializer) from rest_framework.viewsets import GenericViewSet from cron_descriptor import get_description import locale @@ -541,24 +542,7 @@ class UserViewSet(CustomModelViewSet): 获取登录用户信息 """ user = request.user - perms = get_user_perms_map(user, update_cache=True) - data = { - 'id': user.id, - 'username': user.username, - 'type': user.type, - 'name': user.name, - 'roles': user.roles.values_list('name', flat=True), - 'avatar': user.avatar, - 'perms': perms, - 'belong_dept': user.belong_dept.id if user.belong_dept else None, - 'post': user.post.id if user.post else None, - 'belong_dept_name': user.belong_dept.name if user.belong_dept else '', - 'post_name': user.post.name if user.post else '', - 'is_superuser': user.is_superuser, - 'wxmp_openid': user.wxmp_openid, - 'wx_openid': user.wx_openid - } - return Response(data) + return Response(UserFullInfoSerializer(user).data) @action(methods=['post'], detail=False, permission_classes=[IsAuthenticated]) def bind_wxmp(self, request, pk=None): diff --git a/apps/wpm/services.py b/apps/wpm/services.py index 1b7c026d..d1adad78 100644 --- a/apps/wpm/services.py +++ b/apps/wpm/services.py @@ -68,7 +68,7 @@ def get_team_x(sflog: SfLog): teamId_list_len = len(teamId_list) for i in range(len(rule_compare)-teamId_list_len+1): if rule_compare[i:i+teamId_list_len] == teamId_list: - teamId = rule_compare[i+teamId_list_len+1] + teamId = rule_compare[i+teamId_list_len] team = Team.objects.filter(id=teamId).first() return team return None @@ -86,7 +86,10 @@ def make_sflogs(mgroup: Mgroup, start_date: datetime.date, end_date: datetime.da end_time = datetime.datetime.combine(current_date, end_time_o) # 以下代码是解决跨天排班时生成当天班次缺少的bug if start_time > end_time: - end_time += datetime.timedelta(days=1) + if end_time.hour == 0: + end_time += datetime.timedelta(days=1) + else: + start_time -= datetime.timedelta(days=1) total_sec = (end_time - start_time).total_seconds() # 创建SfLog记录 @@ -105,7 +108,7 @@ def make_sflogs(mgroup: Mgroup, start_date: datetime.date, end_date: datetime.da if team: sflog.team = team sflog.leader = team.leader - sflog.save(update_fields=['team']) + sflog.save() current_date = current_date + datetime.timedelta(days=1) diff --git a/apps/wpm/tasks.py b/apps/wpm/tasks.py index f0968663..0ef81c59 100644 --- a/apps/wpm/tasks.py +++ b/apps/wpm/tasks.py @@ -94,6 +94,9 @@ def cal_exp_duration_sec(stlogId: str='', all=False, now: datetime=None): end_time__lte=st_end) | sf_qs.filter(start_time__lte=st_start, end_time__gte=st_end)).order_by('start_time').distinct() SfLogExp.objects.filter(stlog=stlog).exclude(sflog__in=sf_qs).delete() for ind, sflog in enumerate(sf_qs): + if ind == 0: + stlog.sflog = sflog + stlog.save() sflogexp, _ = SfLogExp.objects.get_or_create(stlog=stlog, sflog=sflog, defaults={ 'stlog': stlog, 'sflog': sflog}) # 计算duration