Compare commits

..

No commits in common. "master" and "2.8.2025092816" have entirely different histories.

34 changed files with 492 additions and 1050 deletions

View File

@ -62,7 +62,7 @@ class Mpoint(CommonBModel):
cal_coefficient = models.FloatField("计算系数", null=True, blank=True) cal_coefficient = models.FloatField("计算系数", null=True, blank=True)
mpoint_from = models.ForeignKey("self", verbose_name="来源自采测点", related_name="mp_mpoint_from", on_delete=models.SET_NULL, null=True, blank=True) mpoint_from = models.ForeignKey("self", verbose_name="来源自采测点", related_name="mp_mpoint_from", on_delete=models.SET_NULL, null=True, blank=True)
cal_related_mgroup_running = models.PositiveSmallIntegerField("与工段运行状态的关联", default=10, choices=[(10, "不涉及"), (20, "运行时统计")], null=True, blank=True) cal_related_mgroup_running = models.PositiveSmallIntegerField("与工段运行状态的关联", default=10, choices=[(10, "运行时统计")], null=True, blank=True)
@classmethod @classmethod
def cache_key(cls, code: str): def cache_key(cls, code: str):

View File

@ -167,11 +167,11 @@ def get_first_stlog_time_from_duration(mgroup:Mgroup, dt_start:datetime, dt_end:
if st: if st:
return st, "ending" return st, "ending"
st = st_qs.filter(start_time__gte=dt_start, start_time__lte=dt_end, duration_sec__gte=600).order_by("start_time").last() st = st_qs.filter(start_time__gte=dt_start, start_time__lte=dt_end, duration_sec__lte=600).order_by("start_time").last()
if st: if st:
return st, "start" return st, "start"
st = st_qs.filter(end_time__gte=dt_start, end_time__lte=dt_end, duration_sec__gte=600).order_by("end_time").first() st = st_qs.filter(end_time__gte=dt_start, end_time__lte=dt_end, duration_sec__lte=600).order_by("end_time").first()
if st: if st:
return st, "end" return st, "end"
@ -213,7 +213,7 @@ def cal_mpointstat_hour(mpointId: str, year: int, month: int, day: int, hour: in
val = abs(first_val - last_val) val = abs(first_val - last_val)
else: else:
xtype = "normal" xtype = "normal"
if mpointfrom and mpoint.cal_related_mgroup_running == 20: if mpointfrom and mpoint.cal_related_mgroup_running == 10:
stlog, xtype = get_first_stlog_time_from_duration(mpoint.mgroup, dt, dt_hour_n) stlog, xtype = get_first_stlog_time_from_duration(mpoint.mgroup, dt, dt_hour_n)

View File

@ -37,9 +37,7 @@ class MioFilter(filters.FilterSet):
"item_mio__test_user": ["isnull"], "item_mio__test_user": ["isnull"],
"item_mio__w_mioitem__number": ["exact"], "item_mio__w_mioitem__number": ["exact"],
"mgroup": ["exact"], "mgroup": ["exact"],
"item_mio__batch": ["exact"], "item_mio__batch": ["exact"]
"inout_date": ["gte", "lte", "exact"],
"belong_dept": ["exact"]
} }
def filter_materials__type(self, queryset, name, value): def filter_materials__type(self, queryset, name, value):

View File

@ -10,7 +10,7 @@ from apps.wpmw.models import Wpr
from apps.qm.models import Ftest, Defect from apps.qm.models import Ftest, Defect
from django.db.models import Count, Q from django.db.models import Count, Q
def do_out(item: MIOItem, is_reverse: bool = False): def do_out(item: MIOItem):
""" """
生产领料到车间 生产领料到车间
""" """
@ -23,6 +23,8 @@ def do_out(item: MIOItem, is_reverse: bool = False):
mgroup = mio.mgroup mgroup = mio.mgroup
do_user = mio.do_user do_user = mio.do_user
material:Material = item.material material:Material = item.material
if material.into_wm is False: # 用于混料的原料不与车间库存交互, 这个是配置项目
return
# 获取defect # 获取defect
defect:Defect = None defect:Defect = None
@ -96,24 +98,22 @@ def do_out(item: MIOItem, is_reverse: bool = False):
else: else:
mb.save() mb.save()
if material.into_wm:
# 领到车间库存(或工段) # 领到车间库存(或工段)
wm, new_create = WMaterial.objects.get_or_create( wm, new_create = WMaterial.objects.get_or_create(
batch=xbatch, material=xmaterial, batch=xbatch, material=xmaterial,
belong_dept=belong_dept, mgroup=mgroup, belong_dept=belong_dept, mgroup=mgroup,
state=WMaterial.WM_OK, defect=defect) state=WMaterial.WM_OK, defect=defect)
if new_create: if new_create:
wm.create_by = do_user wm.create_by = do_user
wm.batch_ofrom = mb.batch if mb else None wm.batch_ofrom = mb.batch if mb else None
wm.material_ofrom = mb.material if mb else None wm.material_ofrom = mb.material if mb else None
wm.count = wm.count + item.count wm.count = wm.count + item.count
wm.update_by = do_user wm.update_by = do_user
wm.save() wm.save()
# 开始变动wpr # 开始变动wpr
if xmaterial.tracking == Material.MA_TRACKING_SINGLE: if xmaterial.tracking == Material.MA_TRACKING_SINGLE:
if material.into_wm is False:
raise ParseError("追踪单个物料不支持不进行车间库存的操作")
mioitemws = MIOItemw.objects.filter(mioitem=item) mioitemws = MIOItemw.objects.filter(mioitem=item)
if mioitemws.count() != item.count: if mioitemws.count() != item.count:
raise ParseError("出入库与明细数量不一致,操作失败") raise ParseError("出入库与明细数量不一致,操作失败")
@ -141,7 +141,8 @@ def do_in(item: MIOItem):
mgroup = mio.mgroup mgroup = mio.mgroup
do_user = mio.do_user do_user = mio.do_user
material = item.material material = item.material
if material.into_wm is False: # 根据配置不进行入车间库存的处理
return
action_list = [] action_list = []
mias = MIOItemA.objects.filter(mioitem=item) mias = MIOItemA.objects.filter(mioitem=item)
is_zhj = False # 是否组合件入仓库 is_zhj = False # 是否组合件入仓库
@ -176,39 +177,38 @@ def do_in(item: MIOItem):
raise ParseError("存在非正数!") raise ParseError("存在非正数!")
xbatchs.append(xbatch) xbatchs.append(xbatch)
if material.into_wm:
wm_qs = WMaterial.objects.filter( wm_qs = WMaterial.objects.filter(
batch=xbatch, batch=xbatch,
material=xmaterial, material=xmaterial,
belong_dept=belong_dept, belong_dept=belong_dept,
mgroup=mgroup, mgroup=mgroup,
defect=defect, defect=defect,
state=WMaterial.WM_OK) state=WMaterial.WM_OK)
count_x = wm_qs.count() count_x = wm_qs.count()
if count_x == 1: if count_x == 1:
wm = wm_qs.first() wm = wm_qs.first()
elif count_x == 0: elif count_x == 0:
raise ParseError( raise ParseError(
f'{str(xmaterial)}-{xbatch}-批次库存不存在!') f'{str(xmaterial)}-{xbatch}-批次库存不存在!')
else: else:
raise ParseError( raise ParseError(
f'{str(xmaterial)}-{xbatch}-存在多个相同批次!') f'{str(xmaterial)}-{xbatch}-存在多个相同批次!')
# 扣减车间库存
new_count = wm.count - xcount
if new_count >= 0:
wm.count = new_count
wm.update_by = do_user
wm.save()
else:
raise ParseError(f'{str(xmaterial)}-{xbatch}车间物料不足')
wm_production_dept = wm.mgroup.belong_dept if wm.mgroup else wm.belong_dept
if production_dept is None:
production_dept = wm_production_dept
elif wm_production_dept and production_dept != wm_production_dept:
raise ParseError(f'{str(xmaterial)}-{xbatch}车间物料不属于同一车间')
# 扣减车间库存
new_count = wm.count - xcount
if new_count >= 0:
wm.count = new_count
wm.update_by = do_user
wm.save()
else:
raise ParseError(f'{str(xmaterial)}-{xbatch}车间物料不足')
wm_production_dept = wm.mgroup.belong_dept if wm.mgroup else wm.belong_dept
if production_dept is None:
production_dept = wm_production_dept
elif wm_production_dept and production_dept != wm_production_dept:
raise ParseError(f'{str(xmaterial)}-{xbatch}车间物料不属于同一车间')
# 增加mb # 增加mb
if not is_zhj: if not is_zhj:
mb, _ = MaterialBatch.objects.get_or_create( mb, _ = MaterialBatch.objects.get_or_create(
@ -231,8 +231,6 @@ def do_in(item: MIOItem):
# 开始变动wpr # 开始变动wpr
if xmaterial.tracking == Material.MA_TRACKING_SINGLE: if xmaterial.tracking == Material.MA_TRACKING_SINGLE:
if material.into_wm is False:
raise ParseError("追踪单个物料不支持不进行车间库存的操作")
mioitemws = MIOItemw.objects.filter(mioitem=item) mioitemws = MIOItemw.objects.filter(mioitem=item)
if mioitemws.count() != item.count: if mioitemws.count() != item.count:
raise ParseError("出入库与明细数量不一致,操作失败") raise ParseError("出入库与明细数量不一致,操作失败")

View File

@ -39,7 +39,7 @@ class MaterialViewSet(CustomModelViewSet):
ordering = ['name', 'model', 'specification', ordering = ['name', 'model', 'specification',
'type', 'process', 'process__sort', 'sort', 'id', 'number'] 'type', 'process', 'process__sort', 'sort', 'id', 'number']
ordering_fields = ['name', 'model', 'specification', ordering_fields = ['name', 'model', 'specification',
'type', 'process', 'process__sort', 'sort', 'id', 'number', 'create_time'] 'type', 'process', 'process__sort', 'sort', 'id', 'number']
def perform_destroy(self, instance): def perform_destroy(self, instance):
from apps.inm.models import MaterialBatch from apps.inm.models import MaterialBatch

View File

@ -1,35 +0,0 @@
# Generated by Django 3.2.12 on 2025-09-29 07:51
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('system', '0006_auto_20241213_1249'),
('ofm', '0015_alter_vehicle_end_km'),
]
operations = [
migrations.AddField(
model_name='mroombooking',
name='belong_dept',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mroombooking_belong_dept', to='system.dept', verbose_name='所属部门'),
),
migrations.AddField(
model_name='mroombooking',
name='key_participants',
field=models.TextField(blank=True, null=True, verbose_name='主要参会领导'),
),
migrations.AddField(
model_name='mroombooking',
name='note',
field=models.TextField(blank=True, null=True, verbose_name='备注'),
),
migrations.AddField(
model_name='mroombooking',
name='participant_count',
field=models.PositiveIntegerField(default=0, verbose_name='参会人数'),
),
]

View File

@ -1,23 +0,0 @@
# Generated by Django 3.2.12 on 2025-10-10 08:31
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('ofm', '0016_auto_20250929_1551'),
]
operations = [
migrations.AddField(
model_name='publicity',
name='secret_period',
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='秘密期限'),
),
migrations.AlterField(
model_name='publicity',
name='level',
field=models.JSONField(default=list, help_text=['重要', '一般', '非涉密'], verbose_name='涉密等级'),
),
]

View File

@ -1,33 +0,0 @@
# Generated by Django 3.2.12 on 2025-10-11 01:22
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('ofm', '0017_auto_20251010_1631'),
]
operations = [
migrations.AlterField(
model_name='publicity',
name='dept_opinion',
field=models.JSONField(blank=True, default=list, help_text=['同意', '不同意'], null=True, verbose_name='部门负责人意见'),
),
migrations.AlterField(
model_name='publicity',
name='number',
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='记录编号'),
),
migrations.AlterField(
model_name='publicity',
name='publicity_opinion',
field=models.JSONField(blank=True, default=list, help_text=['同意公开宣传报道', '不同意任何渠道的宣传报道'], null=True, verbose_name='宣传统战部审查意见'),
),
migrations.AlterField(
model_name='publicity',
name='review',
field=models.JSONField(blank=True, default=list, help_text=['内容不涉及国家秘密和商业秘密,申请公开', '内容涉及国家秘密,申请按涉密渠道发布'], null=True, verbose_name='第一撰稿人自审'),
),
]

View File

@ -1,28 +0,0 @@
# Generated by Django 3.2.12 on 2025-10-11 03:28
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('ofm', '0018_auto_20251011_0922'),
]
operations = [
migrations.AlterField(
model_name='publicity',
name='dept_opinion',
field=models.JSONField(default=list, help_text=['同意', '不同意'], verbose_name='部门负责人意见'),
),
migrations.AlterField(
model_name='publicity',
name='publicity_opinion',
field=models.JSONField(blank=True, default=list, help_text=['同意公开宣传报道', '不同意任何渠道的宣传报道'], verbose_name='宣传统战部审查意见'),
),
migrations.AlterField(
model_name='publicity',
name='review',
field=models.JSONField(blank=True, default=list, help_text=['内容不涉及国家秘密和商业秘密,申请公开', '内容涉及国家秘密,申请按涉密渠道发布'], verbose_name='第一撰稿人自审'),
),
]

View File

@ -1,28 +0,0 @@
# Generated by Django 3.2.12 on 2025-10-11 06:27
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('ofm', '0019_auto_20251011_1128'),
]
operations = [
migrations.AlterField(
model_name='publicity',
name='dept_opinion',
field=models.JSONField(blank=True, default=list, help_text=['同意', '不同意'], null=True, verbose_name='部门负责人意见'),
),
migrations.AlterField(
model_name='publicity',
name='publicity_opinion',
field=models.JSONField(blank=True, default=list, help_text=['同意公开宣传报道', '不同意任何渠道的宣传报道'], null=True, verbose_name='宣传统战部审查意见'),
),
migrations.AlterField(
model_name='publicity',
name='review',
field=models.JSONField(blank=True, default=list, help_text=['内容不涉及国家秘密和商业秘密,申请公开', '内容涉及国家秘密,申请按涉密渠道发布'], null=True, verbose_name='第一撰稿人自审'),
),
]

View File

@ -1,18 +0,0 @@
# Generated by Django 3.2.12 on 2025-10-13 01:01
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('ofm', '0020_auto_20251011_1427'),
]
operations = [
migrations.AlterField(
model_name='publicity',
name='publicity_opinion',
field=models.CharField(blank=True, max_length=100, null=True, verbose_name='宣传报道意见'),
),
]

View File

@ -1,17 +0,0 @@
# Generated by Django 3.2.12 on 2025-10-17 06:50
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('ofm', '0021_alter_publicity_publicity_opinion'),
]
operations = [
migrations.AlterUniqueTogether(
name='mroomslot',
unique_together=set(),
),
]

View File

@ -1,49 +0,0 @@
# Generated by Django 3.2.12 on 2025-10-21 06:08
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('wf', '0004_workflow_view_path2'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('system', '0006_auto_20241213_1249'),
('ofm', '0022_alter_mroomslot_unique_together'),
]
operations = [
migrations.CreateModel(
name='PatentInfo',
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=100, verbose_name='拟申请专利名称')),
('author', models.CharField(max_length=100, verbose_name='发明人(设计人)')),
('type', models.CharField(choices=[('invention', '发明专利'), ('utility', '实用新型专利'), ('design', '外观设计专利')], default='invention', max_length=50, verbose_name='专利类型')),
('is_public', models.BooleanField(default=False, verbose_name='是否公开')),
('area', models.CharField(choices=[('Domestic', '国内申请'), ('Foreign', '国外申请'), (' PCT', 'PCT申请')], default='Domestic', max_length=50, verbose_name='拟申请地域')),
('identified', models.BooleanField(default=False, verbose_name='是否进行过科技成果鉴定')),
('published_article', models.BooleanField(default=False, verbose_name='是否发表过文章')),
('exhibited', models.BooleanField(default=False, verbose_name='是否参与过展会展出')),
('applied_to_production', models.BooleanField(default=False, verbose_name='是否参与应用于生产/销售')),
('participated_in_exchange', models.BooleanField(default=False, verbose_name='是否参与过技术交流')),
('tech_background_pages', models.PositiveIntegerField(blank=True, null=True, verbose_name='技术背景材料页数')),
('tech_disclosure_pages', models.PositiveIntegerField(blank=True, null=True, verbose_name='技术交底材料页数')),
('novelty_report_pages', models.PositiveIntegerField(blank=True, null=True, verbose_name='查新检索报告页数')),
('diagrams_or_photos_pages', models.PositiveIntegerField(blank=True, null=True, verbose_name='图/照片页数或张数')),
('belong_dept', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='patentinfo_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='patentinfo_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('ticket', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='patentInfo_ticket', to='wf.ticket', verbose_name='关联工单')),
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='patentinfo_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
],
options={
'abstract': False,
},
),
]

View File

@ -1,95 +0,0 @@
# Generated by Django 3.2.12 on 2025-10-22 02:05
import apps.ofm.models
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('ofm', '0023_patentinfo'),
]
operations = [
migrations.RemoveField(
model_name='patentinfo',
name='applied_to_production',
),
migrations.RemoveField(
model_name='patentinfo',
name='diagrams_or_photos_pages',
),
migrations.RemoveField(
model_name='patentinfo',
name='exhibited',
),
migrations.RemoveField(
model_name='patentinfo',
name='identified',
),
migrations.RemoveField(
model_name='patentinfo',
name='novelty_report_pages',
),
migrations.RemoveField(
model_name='patentinfo',
name='participated_in_exchange',
),
migrations.RemoveField(
model_name='patentinfo',
name='published_article',
),
migrations.RemoveField(
model_name='patentinfo',
name='tech_background_pages',
),
migrations.RemoveField(
model_name='patentinfo',
name='tech_disclosure_pages',
),
migrations.AddField(
model_name='patentinfo',
name='other_area',
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='其它申请地域'),
),
migrations.AddField(
model_name='patentinfo',
name='tech_file',
field=models.JSONField(default=list, help_text='技术文件信息列表每个条目包含name(名称)page(页数)字段', verbose_name='技术文件'),
),
migrations.AddField(
model_name='patentinfo',
name='tech_status',
field=models.JSONField(blank=True, default=apps.ofm.models.PatentInfo.default_document_state, help_text='技术状态信息列表每个条目包含name(名称)、status(状态)、file(文件)字段', verbose_name='技术状态'),
),
migrations.AlterField(
model_name='borrowrecord',
name='remark',
field=models.JSONField(default=list, help_text="['借阅', '复印', '查阅']", verbose_name='用途'),
),
migrations.AlterField(
model_name='publicity',
name='channel',
field=models.JSONField(default=list, help_text="['互联网', '信息平台', '官微', '公开发行物', '其它']", verbose_name='发布渠道'),
),
migrations.AlterField(
model_name='publicity',
name='content',
field=models.JSONField(default=list, help_text="['武器装备科研生产综合事项', '其它']", verbose_name='稿件内容涉及'),
),
migrations.AlterField(
model_name='publicity',
name='dept_opinion',
field=models.JSONField(blank=True, default=list, help_text="['同意', '不同意']", null=True, verbose_name='部门负责人意见'),
),
migrations.AlterField(
model_name='publicity',
name='level',
field=models.JSONField(default=list, help_text="['重要', '一般', '非涉密']", verbose_name='涉密等级'),
),
migrations.AlterField(
model_name='publicity',
name='review',
field=models.JSONField(blank=True, default=list, help_text="['内容不涉及国家秘密和商业秘密,申请公开', '内容涉及国家秘密,申请按涉密渠道发布']", null=True, verbose_name='第一撰稿人自审'),
),
]

View File

@ -3,7 +3,6 @@ from apps.utils.models import CommonADModel, BaseModel, CommonBDModel
from apps.system.models import User from apps.system.models import User
from django.core.validators import RegexValidator from django.core.validators import RegexValidator
from datetime import datetime from datetime import datetime
from rest_framework.exceptions import ParseError
# Create your models here. # Create your models here.
@ -26,15 +25,11 @@ class Mroom(CommonADModel):
location = models.CharField('位置', max_length=100) location = models.CharField('位置', max_length=100)
capacity = models.PositiveIntegerField('容纳人数') capacity = models.PositiveIntegerField('容纳人数')
class MroomBooking(CommonBDModel): class MroomBooking(CommonADModel):
"""TN: 会议室预定信息""" """TN: 会议室预定信息"""
# belong_dept 是预定部门
title = models.CharField('会议主题', max_length=100) title = models.CharField('会议主题', max_length=100)
ticket = models.ForeignKey('wf.ticket', verbose_name='关联会议室', ticket = models.ForeignKey('wf.ticket', verbose_name='关联会议室',
on_delete=models.SET_NULL, related_name='mrooms_ticket', null=True, blank=True, db_constraint=False) on_delete=models.SET_NULL, related_name='mrooms_ticket', null=True, blank=True, db_constraint=False)
note = models.TextField('备注', null=True, blank=True)
participant_count = models.PositiveIntegerField('参会人数', default=0)
key_participants = models.TextField("主要参会领导", null=True, blank=True)
class MroomSlot(BaseModel): class MroomSlot(BaseModel):
@ -44,6 +39,9 @@ class MroomSlot(BaseModel):
mdate = models.DateField('会议日期', db_index=True) mdate = models.DateField('会议日期', db_index=True)
slot = models.PositiveIntegerField('时段', help_text='0-47') slot = models.PositiveIntegerField('时段', help_text='0-47')
is_inuse = models.BooleanField('是否占用', default=True) is_inuse = models.BooleanField('是否占用', default=True)
class Meta:
unique_together = ('mroom', 'mdate', 'slot', 'is_inuse')
# class Seal(BaseModel): # class Seal(BaseModel):
@ -86,10 +84,7 @@ class Vehicle(CommonBDModel):
on_delete=models.SET_NULL, related_name='vehicle_ticket', null=True, blank=True, db_constraint=False) on_delete=models.SET_NULL, related_name='vehicle_ticket', null=True, blank=True, db_constraint=False)
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
if self.end_km: if self.end_km:
if self.start_km <= self.end_km: self.actual_km = self.end_km - self.start_km
self.actual_km = self.end_km - self.start_km
else:
raise ParseError('归还公里数不能小于出发公里数')
else: else:
self.actual_km = 0 self.actual_km = 0
return super().save(*args, **kwargs) return super().save(*args, **kwargs)
@ -112,79 +107,82 @@ class BorrowRecord(CommonBDModel):
borrow_date = models.DateField('借阅日期', null=True, blank=True) borrow_date = models.DateField('借阅日期', null=True, blank=True)
return_date = models.DateField('归还日期', null=True, blank=True) return_date = models.DateField('归还日期', null=True, blank=True)
contacts = models.CharField('借阅人电话', max_length=50, validators=[phone_validator], null=True, blank=True) contacts = models.CharField('借阅人电话', max_length=50, validators=[phone_validator], null=True, blank=True)
remark = models.JSONField('用途', default=list, help_text=str(['借阅', '复印', '查阅'])) remark = models.JSONField('用途', default=list, help_text=['借阅', '复印', '查阅'])
ticket = models.ForeignKey('wf.ticket', verbose_name='关联工单', ticket = models.ForeignKey('wf.ticket', verbose_name='关联工单',
on_delete=models.SET_NULL, related_name='borrow_ticket', null=True, blank=True, db_constraint=False) on_delete=models.SET_NULL, related_name='borrow_ticket', null=True, blank=True, db_constraint=False)
class Publicity(CommonBDModel): class Publicity(CommonBDModel):
"""TN: 公示栏""" """TN: 公示栏"""
number = models.CharField('记录编号', max_length=50, blank=True, null=True) number = models.CharField('记录编号', max_length=50)
title = models.CharField('送审稿件标题', max_length=100) title = models.CharField('送审稿件标题', max_length=100)
participants = models.CharField('所有撰稿人', max_length=50) participants = models.CharField('所有撰稿人', max_length=50)
pub_dept = models.CharField('部室/研究院', null=True, blank=True, max_length=50) pub_dept = models.CharField('部室/研究院', null=True, blank=True, max_length=50)
pfile = models.CharField('稿件路径', null=True, blank=True, max_length=100) pfile = models.CharField('稿件路径', null=True, blank=True, max_length=100)
level = models.JSONField('涉密等级', default=list, help_text=str(['重要', '一般', '非涉密'])) level = models.JSONField('用途', default=list, help_text=['重要', '一般', '非涉密'])
content = models.JSONField('稿件内容涉及', default=list, help_text=str([ content = models.JSONField('稿件内容涉及', default=list, help_text=[
"武器装备科研生产综合事项", "武器装备科研生产综合事项",
"其它" "其它"
])) ])
other_content = models.CharField('其它内容', max_length=100, blank=True, null=True) other_content = models.CharField('其它内容', max_length=100, blank=True, null=True)
report_purpose = models.CharField('宣传报道目的', max_length=100, blank=True, null=True) report_purpose = models.CharField('宣传报道目的', max_length=100, blank=True, null=True)
channel = models.JSONField('发布渠道', default=list, help_text=str(['互联网', '信息平台', '官微', '公开发行物', '其它'])) channel = models.JSONField('发布渠道', default=list, help_text=['互联网', '信息平台', '官微', '公开发行物', '其它'])
other_channel = models.CharField('其它渠道', max_length=50, blank=True, null=True) other_channel = models.CharField('其它渠道', max_length=50, blank=True, null=True)
report_name = models.CharField('报道名称', max_length=50, blank=True, null=True) report_name = models.CharField('报道名称', max_length=50, blank=True, null=True)
review = models.JSONField('第一撰稿人自审', default=list, help_text=str(['内容不涉及国家秘密和商业秘密,申请公开', '内容涉及国家秘密,申请按涉密渠道发布']), null=True,blank=True) review = models.JSONField('第一撰稿人自审', default=list, help_text=['内容不涉及国家秘密和商业秘密,申请公开', '内容涉及国家秘密,申请按涉密渠道发布'])
dept_opinion = models.JSONField('部门负责人意见', default=list, help_text=str(['同意', '不同意']), null=True, blank=True) dept_opinion = models.JSONField('部门负责人意见', default=list, help_text=['同意', '不同意'])
secret_period = models.CharField('秘密期限', max_length=50, blank=True, null=True)
dept_opinion_review = models.CharField('部门审查意见', max_length=100, blank=True, null=True) dept_opinion_review = models.CharField('部门审查意见', max_length=100, blank=True, null=True)
publicity_opinion = models.CharField('宣传报道意见', max_length=100, blank=True, null=True) publicity_opinion = models.JSONField('宣传统战部审查意见', default=list, help_text=['同意公开宣传报道', '不同意任何渠道的宣传报道'])
ticket = models.ForeignKey('wf.ticket', verbose_name='关联工单', ticket = models.ForeignKey('wf.ticket', verbose_name='关联工单',
on_delete=models.SET_NULL, related_name='publicity_ticket', null=True, blank=True, db_constraint=False) on_delete=models.SET_NULL, related_name='publicity_ticket', null=True, blank=True, db_constraint=False)
# 记录编号自动生成 # 记录编号自动生成
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
if not self.number: if not self.number:
last_number = self.__class__.objects.filter(number__startswith=f"GXKG-{datetime.now().year}-").order_by('-number').first() with transaction.atomic():
if last_number: # 加行锁,防止并发取到相同 last_number
try: last_number = self.__class__.objects.select_for_update(skip_locked=True).order_by('-id').first() # skip_locked 锁定行,避免并发冲突
last_num = int(last_number.number.split('-')[-1]) if last_number:
except ValueError: try:
last_num = 0 last_num = int(last_number.number.split('-')[-1])
else: except ValueError:
last_num =0 last_num = 0
# 格式化编号,带补零 new_num= last_num + 1
self.number = f"GXKG-{datetime.now().year}-{last_num+1:02d}" else:
new_num = 1
# 格式化编号,带补零
self.number = f"(GXKG-{datetime.now().year}-{new_num:02d})"
super().save(*args, **kwargs) super().save(*args, **kwargs)
class PatentInfo(CommonBDModel): # class PatentInfo(CommonBDModel):
"""TN: 专利申密审批表单样式""" # """TN: 专利申密审批表单样式"""
PATENT_TYPE_CHOICES = ( # PATENT_TYPE_CHOICES = (
('invention', '发明专利'), # ('invention', '发明专利'),
('utility', '实用新型专利'), # ('utility', '实用新型专利'),
('design', '外观设计专利'), # ('design', '外观设计专利'),
) # )
APPLY_AREAS = ( # APPLY_AREAS = (
('Domestic', '国内申请'), # ('Domestic', '国内申请'),
('Foreign', '国外申请'), # ('Foreign', '国外申请'),
(' PCT', 'PCT申请'), # (' PCT', 'PCT申请'),
) # )
# name = models.CharField('拟申请专利名称', max_length=100)
# author = models.CharField('发明人(设计人)', max_length=100)
# type = models.CharField('专利类型', max_length=50, choices=PATENT_TYPE_CHOICES, default='invention')
# is_public = models.BooleanField('是否公开', default=False)
# area = models.CharField('拟申请地域', max_length=50, choices=APPLY_AREAS, default='Domestic')
# identified = models.BooleanField('是否进行过科技成果鉴定', default=False)
# published_article = models.BooleanField('是否发表过文章', default=False)
# exhibited = models.BooleanField('是否参与过展会展出', default=False)
# applied_to_production = models.BooleanField('是否参与应用于生产/销售', default=False)
# participated_in_exchange = models.BooleanField('是否参与过技术交流', default=False)
# tech_background_pages = models.PositiveIntegerField('技术背景材料页数', null=True, blank=True)
# tech_disclosure_pages = models.PositiveIntegerField('技术交底材料页数', null=True, blank=True)
# novelty_report_pages = models.PositiveIntegerField('查新检索报告页数', null=True, blank=True)
# diagrams_or_photos_pages = models.PositiveIntegerField('图/照片页数或张数', null=True, blank=True)
def default_document_state(self):
return []
name = models.CharField('拟申请专利名称', max_length=100)
author = models.CharField('发明人(设计人)', max_length=100)
type = models.CharField('专利类型', max_length=50, choices=PATENT_TYPE_CHOICES, default='invention')
is_public = models.BooleanField('是否公开', default=False)
area = models.CharField('拟申请地域', max_length=50, choices=APPLY_AREAS, default='Domestic')
other_area = models.CharField('其它申请地域', max_length=50, blank=True, null=True)
tech_status = models.JSONField('技术状态', default=default_document_state, blank=True, help_text='技术状态信息列表每个条目包含name(名称)、status(状态)、file(文件)字段')
tech_file = models.JSONField('技术文件', default=list, help_text='技术文件信息列表每个条目包含name(名称)page(页数)字段')
ticket = models.ForeignKey('wf.ticket', verbose_name='关联工单',
on_delete=models.SET_NULL, related_name='patentInfo_ticket', null=True, blank=True, db_constraint=False)
# class PaperOfm(CommonADModel): # class PaperOfm(CommonADModel):
# """TN: 论文申密审批表单""" # """TN: 论文申密审批表单"""
# PAPER_TYPE_CHOICES = ( # PAPER_TYPE_CHOICES = (

View File

@ -1,5 +1,5 @@
from .models import (Mroom, MroomBooking, MroomSlot, LendingSeal, Vehicle, FileRecord, BorrowRecord, Publicity, PatentInfo) from .models import (Mroom, MroomBooking, MroomSlot, LendingSeal, Vehicle, FileRecord, BorrowRecord, Publicity)
# Publicity, PatetInfo, PaperOfm, Platform, Project, PatentRecord, PaperRecord, ProjectApproval, ProjectInfo) # Publicity, PatentInfo, PaperOfm, Platform, Project, PatentRecord, PaperRecord, ProjectApproval, ProjectInfo)
from apps.utils.serializers import CustomModelSerializer from apps.utils.serializers import CustomModelSerializer
from rest_framework import serializers from rest_framework import serializers
from django.db import transaction from django.db import transaction
@ -19,14 +19,11 @@ class MroomBookingSerializer(CustomModelSerializer):
mdate = serializers.DateField(write_only=True, label="预订日期") mdate = serializers.DateField(write_only=True, label="预订日期")
slots = serializers.ListField(child=serializers.IntegerField(), write_only=True, label="时段索引") slots = serializers.ListField(child=serializers.IntegerField(), write_only=True, label="时段索引")
create_by_name = serializers.CharField(source='create_by.username', read_only=True) create_by_name = serializers.CharField(source='create_by.username', read_only=True)
create_by_phone = serializers.CharField(source='create_by.phone', read_only=True)
belong_dept_name = serializers.CharField(source='belong_dept.name', read_only=True)
ticket_ = TicketSimpleSerializer(source='ticket', read_only=True) ticket_ = TicketSimpleSerializer(source='ticket', read_only=True)
class Meta: class Meta:
model = MroomBooking model = MroomBooking
fields = '__all__' fields = '__all__'
read_only_fields = EXCLUDE_FIELDS read_only_fields = EXCLUDE_FIELDS
extra_kwargs = {'belong_dept': {'required': True}}
def create(self, validated_data): def create(self, validated_data):
mroom = validated_data.pop('mroom') mroom = validated_data.pop('mroom')
@ -37,10 +34,10 @@ class MroomBookingSerializer(CustomModelSerializer):
for slot in slots: for slot in slots:
if slot < 0 or slot > 47: if slot < 0 or slot > 47:
raise ParseError("时段索引超出范围") raise ParseError("时段索引超出范围")
ms_exists = MroomSlot.objects.filter(mroom=mroom, mdate=mdate, slot=slot, is_inuse=True).exists() try:
if ms_exists: MroomSlot.objects.create(booking=booking, slot=slot, mdate=mdate, mroom=mroom, is_inuse=True)
raise ParseError("时段已预订,请刷新重选") except Exception as e:
MroomSlot.objects.create(booking=booking, slot=slot, mdate=mdate, mroom=mroom, is_inuse=True) raise ParseError(f"时段已预订,请刷新重选-{e}")
return booking return booking
def update(self, instance, validated_data): def update(self, instance, validated_data):
@ -52,10 +49,10 @@ class MroomBookingSerializer(CustomModelSerializer):
for slot in slots: for slot in slots:
if slot < 0 or slot > 47: if slot < 0 or slot > 47:
raise ParseError("时段索引超出范围") raise ParseError("时段索引超出范围")
ms_exists = MroomSlot.objects.filter(mroom=mroom, mdate=mdate, slot=slot, is_inuse=True).exists() try:
if ms_exists: MroomSlot.objects.create(booking=booking, slot=slot, mdate=mdate, mroom=mroom, is_inuse=True)
raise ParseError("时段已预订,请刷新重选") except Exception as e:
MroomSlot.objects.create(booking=booking, slot=slot, mdate=mdate, mroom=mroom, is_inuse=True) raise ParseError(f"时段已预订,请刷新重选-{e}")
return booking return booking
@ -122,14 +119,11 @@ class PublicitySerializer(CustomModelSerializer):
read_only_fields = EXCLUDE_FIELDS read_only_fields = EXCLUDE_FIELDS
class PatentInfoSerializer(CustomModelSerializer): # class PatentInfoSerializer(CustomModelSerializer):
create_by_name = serializers.CharField(source='create_by.name', read_only=True) # class Meta:
belong_dept_name = serializers.CharField(source='belong_dept.name', read_only=True) # model = PatentInfo
ticket_ = TicketSimpleSerializer(source='ticket', read_only=True) # fields = '__all__'
class Meta: # read_only_fields = EXCLUDE_FIELDS
model = PatentInfo
fields = '__all__'
read_only_fields = EXCLUDE_FIELDS
# class PaperSerializer(CustomModelSerializer): # class PaperSerializer(CustomModelSerializer):

View File

@ -1,7 +1,7 @@
from apps.wf.models import Ticket from apps.wf.models import Ticket
# TicketFlow, Transition, Workflow, CustomField, State, # TicketFlow, Transition, Workflow, CustomField, State,
from apps.ofm.models import LendingSeal, Vehicle, BorrowRecord, Publicity, MroomBooking, MroomSlot, PatentInfo from apps.ofm.models import LendingSeal, Vehicle, BorrowRecord, Publicity, MroomBooking, MroomSlot
from rest_framework.exceptions import ParseError from rest_framework.exceptions import ParseError
@ -31,7 +31,6 @@ def mroombooking_reject(ticket: Ticket, transition, new_ticket_data: dict):
def bind_lendingseal(ticket: Ticket, transition, new_ticket_data: dict): def bind_lendingseal(ticket: Ticket, transition, new_ticket_data: dict):
ins = LendingSeal.objects.get(id=new_ticket_data['t_id']) ins = LendingSeal.objects.get(id=new_ticket_data['t_id'])
ins.actual_return_date = None
ticket_data = ticket.ticket_data ticket_data = ticket.ticket_data
ticket_data.update({ ticket_data.update({
't_model': 'LendingSeal', 't_model': 'LendingSeal',
@ -43,19 +42,6 @@ def bind_lendingseal(ticket: Ticket, transition, new_ticket_data: dict):
if ins.ticket is None: if ins.ticket is None:
ins.ticket = ticket ins.ticket = ticket
ins.save() ins.save()
# 如果驳回到开始状态
def lending_save_ticket_data(ticket: Ticket, new_ticket_data: dict, **kwargs):
try:
obj = LendingSeal.objects.get(id=new_ticket_data['t_id'])
except LendingSeal.DoesNotExist:
raise ParseError("Publicity t_id 不存在")
data_save = {k: v for k, v in new_ticket_data.items() if k not in ['t_model', 't_id']}
for k, v in data_save.items():
setattr(obj, k, v)
obj.save()
def bind_vehicle(ticket: Ticket, transition, new_ticket_data: dict): def bind_vehicle(ticket: Ticket, transition, new_ticket_data: dict):
ins = Vehicle.objects.get(id=new_ticket_data['t_id']) ins = Vehicle.objects.get(id=new_ticket_data['t_id'])
@ -64,8 +50,6 @@ def bind_vehicle(ticket: Ticket, transition, new_ticket_data: dict):
't_model': 'Vehicle', 't_model': 'Vehicle',
't_id': ins.id, 't_id': ins.id,
}) })
ins.actual_km = None
ins.end_time = None
ticket.ticket_data = ticket_data ticket.ticket_data = ticket_data
ticket.create_by = ins.create_by ticket.create_by = ins.create_by
ticket.save() ticket.save()
@ -73,16 +57,6 @@ def bind_vehicle(ticket: Ticket, transition, new_ticket_data: dict):
ins.ticket = ticket ins.ticket = ticket
ins.save() ins.save()
def vehicle_save_ticket_data(ticket: Ticket, new_ticket_data: dict, **kwargs):
try:
obj = Vehicle.objects.get(id=new_ticket_data['t_id'])
except Vehicle.DoesNotExist:
raise ParseError("Publicity t_id 不存在")
data_save = {k: v for k, v in new_ticket_data.items() if k not in ['t_model', 't_id']}
for k, v in data_save.items():
setattr(obj, k, v)
obj.save()
def bind_file(ticket: Ticket, transition, new_ticket_data: dict): def bind_file(ticket: Ticket, transition, new_ticket_data: dict):
ins = BorrowRecord.objects.get(id=new_ticket_data['t_id']) ins = BorrowRecord.objects.get(id=new_ticket_data['t_id'])
@ -91,24 +65,13 @@ def bind_file(ticket: Ticket, transition, new_ticket_data: dict):
't_model': 'BorrowRecord', 't_model': 'BorrowRecord',
't_id': ins.id, 't_id': ins.id,
}) })
ins.return_date = None
ticket.ticket_data = ticket_data ticket.ticket_data = ticket_data
ticket.create_by = ins.create_by ticket.create_by = ins.create_by
ticket.save() ticket.save()
if ins.ticket is None: if ins.ticket is None:
ins.ticket = ticket ins.ticket = ticket
ins.save() ins.save()
def file_save_ticket_data(ticket: Ticket, new_ticket_data: dict, **kwargs):
try:
obj = BorrowRecord.objects.get(id=new_ticket_data['t_id'])
except BorrowRecord.DoesNotExist:
raise ParseError("Publicity t_id 不存在")
data_save = {k: v for k, v in new_ticket_data.items() if k not in ['t_model', 't_id']}
for k, v in data_save.items():
setattr(obj, k, v)
obj.save()
def bind_publicity(ticket: Ticket, transition, new_ticket_data: dict): def bind_publicity(ticket: Ticket, transition, new_ticket_data: dict):
ins = Publicity.objects.get(id=new_ticket_data['t_id']) ins = Publicity.objects.get(id=new_ticket_data['t_id'])
@ -117,36 +80,6 @@ def bind_publicity(ticket: Ticket, transition, new_ticket_data: dict):
't_model': 'publicity', 't_model': 'publicity',
't_id': ins.id, 't_id': ins.id,
}) })
ins.dept_opinion = None
ins.secret_period = None
ins.dept_opinion_review = None
ins.publicity_opinion = None
ticket.ticket_data = ticket_data
ticket.create_by = ins.create_by
ticket.save()
if ins.ticket is None:
ins.ticket = ticket
ins.save()
def save_ticket_data(ticket: Ticket, new_ticket_data: dict, **kwargs):
try:
obj = Publicity.objects.get(id=new_ticket_data['t_id'])
except Publicity.DoesNotExist:
raise ParseError("Publicity t_id 不存在")
data_save = {k: v for k, v in new_ticket_data.items() if k not in ['t_model', 't_id']}
for k, v in data_save.items():
setattr(obj, k, v)
obj.save()
def bind_patent(ticket: Ticket, transition, new_ticket_data: dict):
ins = PatentInfo.objects.get(id=new_ticket_data['t_id'])
ticket_data = ticket.ticket_data
ticket_data.update({
't_model': 'patent',
't_id': ins.id,
})
ticket.ticket_data = ticket_data ticket.ticket_data = ticket_data
ticket.create_by = ins.create_by ticket.create_by = ins.create_by
ticket.save() ticket.save()

View File

@ -1,9 +1,8 @@
from django.urls import path, include from django.urls import path, include
from rest_framework.routers import DefaultRouter from rest_framework.routers import DefaultRouter
from apps.ofm.views import (MroomViewSet, MroomBookingViewSet, MroomSlotViewSet,LendingSealViewSet, VehicleViewSet, FilerecordViewSet, from apps.ofm.views import (MroomViewSet, MroomBookingViewSet, MroomSlotViewSet,LendingSealViewSet, VehicleViewSet, FilerecordViewSet, FileborrowViewSet, PublicityViewSet)
FileborrowViewSet, PublicityViewSet, PatentInfoViewSet)
# SealModelViewSet, # SealModelViewSet,
# , PublicityViewSet, , PaperViewSet, PlatformViewSet, # , PublicityViewSet, PatentInfoViewSet, PaperViewSet, PlatformViewSet,
# ProjectViewSet, PatentRecordViewSet, PaperRecordViewSet, ProjectApprovalViewSet, ProjectInfoViewSet) # ProjectViewSet, PatentRecordViewSet, PaperRecordViewSet, ProjectApprovalViewSet, ProjectInfoViewSet)
API_BASE_URL = 'api/ofm/' API_BASE_URL = 'api/ofm/'
@ -19,7 +18,7 @@ router.register('vehicle', VehicleViewSet, basename='vehicle')
router.register('filerecord', FilerecordViewSet, basename='filerecord') router.register('filerecord', FilerecordViewSet, basename='filerecord')
router.register('fileborrow', FileborrowViewSet, basename='fileborrow') router.register('fileborrow', FileborrowViewSet, basename='fileborrow')
router.register('publicity', PublicityViewSet, basename='publicity') router.register('publicity', PublicityViewSet, basename='publicity')
router.register('patentinfo', PatentInfoViewSet, basename='patentinfo') # router.register('patentinfo', PatentInfoViewSet, basename='patentinfo')
# router.register('paper', PaperViewSet, basename='paper') # router.register('paper', PaperViewSet, basename='paper')
# router.register('platform', PlatformViewSet, basename='platform') # router.register('platform', PlatformViewSet, basename='platform')
# router.register('project', ProjectViewSet, basename='project') # router.register('project', ProjectViewSet, basename='project')

View File

@ -1,9 +1,8 @@
from django.shortcuts import render from django.shortcuts import render
from apps.utils.viewsets import CustomModelViewSet, CustomGenericViewSet from apps.utils.viewsets import CustomModelViewSet, CustomGenericViewSet
from .models import Mroom, MroomBooking, MroomSlot, LendingSeal, Vehicle, FileRecord, BorrowRecord, Publicity, PatentInfo from .models import Mroom, MroomBooking, MroomSlot, LendingSeal, Vehicle, FileRecord, BorrowRecord, Publicity
# Publicity, , PaperOfm, Platform, Project, PatentRecord, PaperRecord, ProjectApproval, ProjectInfo) # Publicity, PatentInfo, PaperOfm, Platform, Project, PatentRecord, PaperRecord, ProjectApproval, ProjectInfo)
from .serializers import (MroomSerializer, MroomBookingSerializer, MroomSlotSerializer, LendingSealSerializer, from .serializers import (MroomSerializer, MroomBookingSerializer, MroomSlotSerializer, LendingSealSerializer, VehicleSerializer, FileRecordSerializer, BorrowRecordSerializer, PublicitySerializer,)
VehicleSerializer, FileRecordSerializer, BorrowRecordSerializer, PublicitySerializer, PatentInfoSerializer)
# ,SealSerializer, # ,SealSerializer,
# LendingSealSerializer, FileRecordSerializer, BorrowRecordSerializer, PublicitySerializer, # LendingSealSerializer, FileRecordSerializer, BorrowRecordSerializer, PublicitySerializer,
# PatentInfoSerializer, PaperSerializer, PlatformSerializer, ProjectSerializer, ProjectMemberSerializer, PaperRecordSerializer, ProjectApprovalSerializer, ProjectInfoSerializer) # PatentInfoSerializer, PaperSerializer, PlatformSerializer, ProjectSerializer, ProjectMemberSerializer, PaperRecordSerializer, ProjectApprovalSerializer, ProjectInfoSerializer)
@ -29,7 +28,7 @@ class MroomBookingViewSet(CustomModelViewSet):
""" """
queryset = MroomBooking.objects.all() queryset = MroomBooking.objects.all()
serializer_class = MroomBookingSerializer serializer_class = MroomBookingSerializer
select_related_fields = ["create_by", "ticket", "belong_dept"] select_related_fields = ["create_by", "ticket"]
filterset_class = MroomBookingFilterset filterset_class = MroomBookingFilterset
def add_info_for_list(self, data): def add_info_for_list(self, data):
@ -67,7 +66,7 @@ class MroomBookingViewSet(CustomModelViewSet):
start_time = self._slot_to_time(info["current_slots"][0]) start_time = self._slot_to_time(info["current_slots"][0])
end_time = self._slot_to_time(info["current_slots"][-1] + 1) end_time = self._slot_to_time(info["current_slots"][-1] + 1)
info["time_ranges"].append(f"{start_time}-{end_time}") info["time_ranges"].append(f"{start_time}-{end_time}")
info["slots"] = info.pop("current_slots") # 清理临时数据 info.pop("current_slots") # 清理临时数据
for item in data: for item in data:
item.update(booking_info.get(item["id"], {})) item.update(booking_info.get(item["id"], {}))
@ -124,7 +123,7 @@ class LendingSealViewSet(CustomModelViewSet):
queryset = LendingSeal.objects.all() queryset = LendingSeal.objects.all()
serializer_class = LendingSealSerializer serializer_class = LendingSealSerializer
filterset_class = SealFilter filterset_class = SealFilter
ordering = ["-create_time"] ordering = ["create_time"]
data_filter = True data_filter = True
@ -135,7 +134,7 @@ class VehicleViewSet(CustomModelViewSet):
""" """
queryset = Vehicle.objects.all() queryset = Vehicle.objects.all()
serializer_class = VehicleSerializer serializer_class = VehicleSerializer
ordering = ["-create_time"] ordering = ["create_time"]
class FilerecordViewSet(CustomModelViewSet): class FilerecordViewSet(CustomModelViewSet):
@ -146,7 +145,7 @@ class FilerecordViewSet(CustomModelViewSet):
queryset = FileRecord.objects.all() queryset = FileRecord.objects.all()
serializer_class = FileRecordSerializer serializer_class = FileRecordSerializer
filterset_fields = [ "name", "number"] filterset_fields = [ "name", "number"]
ordering = ["-create_time", "number", "name"] ordering = ["create_time", "number", "name"]
class FileborrowViewSet(CustomModelViewSet): class FileborrowViewSet(CustomModelViewSet):
@ -157,7 +156,7 @@ class FileborrowViewSet(CustomModelViewSet):
queryset = BorrowRecord.objects.all() queryset = BorrowRecord.objects.all()
serializer_class = BorrowRecordSerializer serializer_class = BorrowRecordSerializer
filterset_class = BorrowRecordFilter filterset_class = BorrowRecordFilter
ordering = ["-create_time"] ordering = ["create_time"]
class PublicityViewSet(CustomModelViewSet): class PublicityViewSet(CustomModelViewSet):
@ -168,18 +167,18 @@ class PublicityViewSet(CustomModelViewSet):
queryset = Publicity.objects.all() queryset = Publicity.objects.all()
serializer_class = PublicitySerializer serializer_class = PublicitySerializer
filterset_fields = ["title","number"] filterset_fields = ["title","number"]
ordering = ["-create_time", "number"] ordering = ["create_time", "number"]
class PatentInfoViewSet(CustomModelViewSet): # class PatentInfoViewSet(CustomModelViewSet):
"""list: 专利 # """list: 专利
专利 # 专利
""" # """
queryset = PatentInfo.objects.all() # queryset = PatentInfo.objects.all()
serializer_class = PatentInfoSerializer # serializer_class = PatentInfoSerializer
filterset_fields = ["name", "author", "type"] # filterset_fields = ["name", "author", "type"]
ordering = ["-create_time", "name", "author", "type"] # ordering = ["create_time", "name", "author", "type"]
# class PaperViewSet(CustomModelViewSet): # class PaperViewSet(CustomModelViewSet):

View File

@ -1,18 +0,0 @@
# Generated by Django 3.2.12 on 2025-10-10 01:32
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('qm', '0053_alter_ftest_is_ok'),
]
operations = [
migrations.AlterField(
model_name='ptest',
name='val_xj',
field=models.CharField(blank=True, choices=[('S', '析晶'), ('R', '不析晶'), ('θ', '未化')], help_text=[('S', '析晶'), ('R', '不析晶'), ('θ', '未化')], max_length=10, null=True, verbose_name='析晶'),
),
]

View File

@ -445,7 +445,7 @@ class Ptest(CommonAModel):
val_tg = models.FloatField("Tg", help_text='', null=True, blank=True) val_tg = models.FloatField("Tg", help_text='', null=True, blank=True)
val_tf = models.FloatField("Tf", help_text='', null=True, blank=True) val_tf = models.FloatField("Tf", help_text='', null=True, blank=True)
val_xj = models.CharField( val_xj = models.CharField(
'析晶', max_length=10, null=True, blank=True, choices=PTEST_XJ_VALS, help_text=list(PTEST_XJ_VALS)) '析晶', max_length=10, default='S', choices=PTEST_XJ_VALS, help_text=list(PTEST_XJ_VALS))
val_pzxs = models.FloatField( val_pzxs = models.FloatField(
'膨胀系数', help_text='30-300℃', null=True, blank=True) '膨胀系数', help_text='30-300℃', null=True, blank=True)
val_zgwd = models.FloatField('升至最高温度', null=True, blank=True) val_zgwd = models.FloatField('升至最高温度', null=True, blank=True)

View File

@ -195,8 +195,6 @@ class CustomRetrieveModelMixin(RetrieveModelMixin):
给dict返回数据添加额外信息 给dict返回数据添加额外信息
""" """
if hasattr(self, 'add_info_for_list'):
return self.add_info_for_list([data])[0]
return data return data
class CustomListModelMixin(ListModelMixin): class CustomListModelMixin(ListModelMixin):
@ -245,8 +243,6 @@ class ComplexQueryMixin:
vdata = sr.validated_data vdata = sr.validated_data
queryset = self.get_queryset() queryset = self.get_queryset()
querys = vdata.get('querys', []) querys = vdata.get('querys', [])
annotate_field_list = vdata.get('annotate_field_list', [])
if not querys: if not querys:
new_qs = queryset new_qs = queryset
else: else:
@ -268,29 +264,17 @@ class ComplexQueryMixin:
new_qs = new_qs | one_qs new_qs = new_qs | one_qs
except Exception as e: except Exception as e:
raise ParseError(str(e)) raise ParseError(str(e))
if annotate_field_list:
annotate_dict = getattr(self, "annotate_dict", {})
if annotate_dict:
filtered_annotate_dict = { key: annotate_dict[key] for key in annotate_field_list if key in annotate_dict}
new_qs = new_qs.annotate(**filtered_annotate_dict)
ordering = vdata.get('ordering', None) ordering = vdata.get('ordering', None)
if not ordering: if not ordering:
ordering = getattr(self, 'ordering', None) ordering = getattr(self, 'ordering', None)
if isinstance(ordering, str): if isinstance(ordering, str):
ordering = ordering.replace('\n', '').replace(' ', '')
ordering = ordering.split(',') ordering = ordering.split(',')
order_fields = []
if ordering: if ordering:
for item in ordering: for item in ordering:
if item.startswith('-'): if item.startswith('-'):
# JSONField 排序只能用字符串,不要 F new_qs = new_qs.order_by(F(item[1:]).desc(nulls_last=True))
order_fields.append(F(item[1:]).desc(nulls_last=True) if '__' not in item else item)
else: else:
order_fields.append(F(item).asc(nulls_last=True) if '__' not in item else item) new_qs = new_qs.order_by(item)
new_qs = new_qs.order_by(*order_fields)
page = self.paginate_queryset(new_qs) page = self.paginate_queryset(new_qs)
if page is not None: if page is not None:
serializer = self.get_serializer(page, many=True) serializer = self.get_serializer(page, many=True)

View File

@ -85,4 +85,3 @@ class ComplexSerializer(serializers.Serializer):
ordering = serializers.CharField(required=False) ordering = serializers.CharField(required=False)
querys = serializers.ListField(child=QuerySerializer( querys = serializers.ListField(child=QuerySerializer(
many=True), label="查询列表", required=False) many=True), label="查询列表", required=False)
annotate_field_list = serializers.ListField(child=serializers.CharField(), label="RawSQL字段列表", required=False)

View File

@ -1,18 +0,0 @@
# Generated by Django 3.2.12 on 2025-10-16 08:29
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('wf', '0003_workflow_view_path'),
]
operations = [
migrations.AddField(
model_name='workflow',
name='view_path2',
field=models.TextField(blank=True, null=True, verbose_name='前端自定义页面路径2'),
),
]

View File

@ -22,7 +22,6 @@ class Workflow(CommonAModel):
content_template = models.CharField( content_template = models.CharField(
'内容模板', max_length=1000, default='标题:{title}, 创建时间:{create_time}', null=True, blank=True, help_text='工单字段的值可以作为参数写到模板中,格式如:标题:{title}, 创建时间:{create_time}') '内容模板', max_length=1000, default='标题:{title}, 创建时间:{create_time}', null=True, blank=True, help_text='工单字段的值可以作为参数写到模板中,格式如:标题:{title}, 创建时间:{create_time}')
view_path = models.TextField('前端自定义页面路径', null=True, blank=True) view_path = models.TextField('前端自定义页面路径', null=True, blank=True)
view_path2 = models.TextField('前端自定义页面路径2', null=True, blank=True)
class Meta: class Meta:
verbose_name = '工作流' verbose_name = '工作流'

View File

@ -77,7 +77,7 @@ class WfService(object):
""" """
获取状态可执行的操作 获取状态可执行的操作
""" """
return Transition.objects.filter(is_deleted=False, source_state=state).all().order_by("-attribute_type", "-id") return Transition.objects.filter(is_deleted=False, source_state=state).all()
@classmethod @classmethod
def get_ticket_steps(cls, ticket: Ticket): def get_ticket_steps(cls, ticket: Ticket):

View File

@ -1,25 +0,0 @@
# Generated by Django 3.2.12 on 2025-10-20 02:09
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('em', '0022_equipment_cd_req_addr'),
('wpm', '0123_mlogbdefect_count_has'),
]
operations = [
migrations.AddField(
model_name='mloguser',
name='equipment',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='mloguser_equipment', to='em.equipment', verbose_name='生产设备'),
),
migrations.AddField(
model_name='mloguser',
name='work_start_time',
field=models.DateTimeField(blank=True, null=True, verbose_name='生产开始时间'),
),
]

View File

@ -368,9 +368,6 @@ class MlogUser(BaseModel):
mlog = models.ForeignKey(Mlog, verbose_name='关联日志', on_delete=models.CASCADE) mlog = models.ForeignKey(Mlog, verbose_name='关联日志', on_delete=models.CASCADE)
handle_user = models.ForeignKey(User, verbose_name='操作人', on_delete=models.CASCADE) handle_user = models.ForeignKey(User, verbose_name='操作人', on_delete=models.CASCADE)
process = models.ForeignKey(Process, verbose_name='子工序', on_delete=models.CASCADE) process = models.ForeignKey(Process, verbose_name='子工序', on_delete=models.CASCADE)
work_start_time = models.DateTimeField('生产开始时间', null=True, blank=True)
equipment = models.ForeignKey(
Equipment, verbose_name='生产设备', on_delete=models.CASCADE, null=True, blank=True, related_name='mloguser_equipment')
shift = models.ForeignKey(Shift, verbose_name='关联班次', on_delete=models.CASCADE) shift = models.ForeignKey(Shift, verbose_name='关联班次', on_delete=models.CASCADE)
handle_date = models.DateField('操作日期') handle_date = models.DateField('操作日期')
@ -527,7 +524,7 @@ class Mlogbw(BaseModel):
mlogb.count_real = count mlogb.count_real = count
count_notok = 0 count_notok = 0
count_notok_full = 0 count_notok_full = 0
# 个追踪的合格b类不分这里会导致count_ok_full与count_ok一样了暂时不做处理 # 个追踪的合格b类不分
tqs = Mlogbw.objects.filter(mlogb=mlogb, ftest__defect_main__isnull=False) tqs = Mlogbw.objects.filter(mlogb=mlogb, ftest__defect_main__isnull=False)
tqs_a = Mlogbw.objects.filter(mlogb=mlogb, ftest__defect_main__isnull=False).values("ftest__defect_main").annotate(xcount=Count('id')) tqs_a = Mlogbw.objects.filter(mlogb=mlogb, ftest__defect_main__isnull=False).values("ftest__defect_main").annotate(xcount=Count('id'))
defect_ids = tqs.values_list("ftest__defect_main", flat=True) defect_ids = tqs.values_list("ftest__defect_main", flat=True)

View File

@ -355,12 +355,12 @@ class MlogSerializer(CustomModelSerializer):
model = Mlog model = Mlog
fields = '__all__' fields = '__all__'
read_only_fields = EXCLUDE_FIELDS + \ read_only_fields = EXCLUDE_FIELDS + \
['submit_time', 'submit_user', 'material_outs'] ['submit_time', 'submit_user', 'material_outs', "handle_date", "shift"]
extra_kwargs = { extra_kwargs = {
"batch": {"required": True}, "batch": {"required": True},
"shift": {"required": False}, "shift": {"required": False},
"material_out": {"required": True}, "material_out": {"required": True},
"work_start_time": {"required": False} "work_start_time": {"required": True}
} }
def create(self, validated_data): def create(self, validated_data):
@ -634,20 +634,14 @@ class MlogSerializer(CustomModelSerializer):
# 时间 # 时间
mgroup:Mgroup = attrs['mgroup'] mgroup:Mgroup = attrs['mgroup']
work_start_time:datetime = attrs.get('work_start_time', None) work_start_time:datetime = attrs['work_start_time']
if work_start_time: handle_date, attrs["shift"] = mgroup.get_shift(work_start_time)
attrs['handle_date'], attrs["shift"] = mgroup.get_shift(work_start_time)
else:
if "handle_date" in attrs and attrs["handle_date"]:
pass
else:
raise ParseError('缺少生产日期')
if mtask and mtask.start_date == mtask.end_date: if mtask and mtask.start_date == mtask.end_date:
if attrs['handle_date'] != mtask.end_date: attrs['handle_date'] = mtask.end_date
if work_start_time: if attrs['handle_date'] != handle_date:
raise ParseError('任务日期与生产日期不一致') raise ParseError('任务日期与生产日期不一致')
else: else:
attrs['handle_date'] = mtask.end_date attrs['handle_date'] = handle_date
handle_user = attrs.get('handle_user', None) handle_user = attrs.get('handle_user', None)
if handle_user is None and hasattr(self, "request"): if handle_user is None and hasattr(self, "request"):
@ -1492,17 +1486,10 @@ class MlogUserSerializer(CustomModelSerializer):
class Meta: class Meta:
model = MlogUser model = MlogUser
fields = "__all__" fields = "__all__"
read_only_fields = EXCLUDE_FIELDS_BASE + ["shift", "handle_date"] read_only_fields = EXCLUDE_FIELDS_BASE
extra_kwargs = {
"work_start_time": {"required": True}
}
def create(self, validated_data): def create(self, validated_data):
mlog:Mlog = validated_data["mlog"] mlog:Mlog = validated_data["mlog"]
work_start_time:datetime = validated_data["work_start_time"]
if mlog.work_start_time < mlog.work_start_time:
raise ParseError("操作时间不能早于日志开始时间")
validated_data["handle_date"], validated_data["shift"] = mlog.mgroup.get_shift(work_start_time)
if mlog.submit_time is not None: if mlog.submit_time is not None:
raise ParseError("该日志已提交") raise ParseError("该日志已提交")
process:Process = validated_data["process"] process:Process = validated_data["process"]

View File

@ -11,7 +11,7 @@ from apps.system.models import User
from apps.pm.models import Mtask from apps.pm.models import Mtask
from apps.mtm.models import Mgroup, Shift, Material, Route, RoutePack, Team, Srule from apps.mtm.models import Mgroup, Shift, Material, Route, RoutePack, Team, Srule
from .models import SfLog, WMaterial, Mlog, Mlogb, Mlogbw, Handover, Handoverb, Handoverbw, MlogbDefect, BatchLog, BatchSt, MlogUser from .models import SfLog, WMaterial, Mlog, Mlogb, Mlogbw, Handover, Handoverb, Handoverbw, MlogbDefect, BatchLog, BatchSt
from apps.mtm.services_2 import cal_material_count from apps.mtm.services_2 import cal_material_count
from apps.wf.models import Ticket from apps.wf.models import Ticket
from apps.wf.services import WfService from apps.wf.services import WfService
@ -152,7 +152,7 @@ def mlog_submit(mlog: Mlog, user: User, now: Union[datetime.datetime, None]):
""" """
生产日志提交后需要执行的操作 生产日志提交后需要执行的操作
""" """
if mlog.work_start_time and mlog.work_start_time > timezone.now(): if mlog.work_start_time > timezone.now():
raise ParseError('操作开始时间不能晚于当前时间') raise ParseError('操作开始时间不能晚于当前时间')
if mlog.work_start_time and mlog.work_end_time and mlog.work_end_time < mlog.work_start_time: if mlog.work_start_time and mlog.work_end_time and mlog.work_end_time < mlog.work_start_time:
raise ParseError('操作结束时间不能早于操作开始时间') raise ParseError('操作结束时间不能早于操作开始时间')
@ -385,7 +385,7 @@ def mlog_submit(mlog: Mlog, user: User, now: Union[datetime.datetime, None]):
mlog.submit_user = user mlog.submit_user = user
mlog.stored_notok = stored_notok mlog.stored_notok = stored_notok
mlog.stored_mgroup = stored_mgroup mlog.stored_mgroup = stored_mgroup
if mlog.work_end_time is None and mlog.work_start_time is not None: if mlog.work_end_time is None:
mlog.work_end_time = now mlog.work_end_time = now
mlog.save() mlog.save()
@ -1026,10 +1026,6 @@ def mlog_submit_validate(ins: Mlog):
raise ParseError('该日志未指定消耗!') raise ParseError('该日志未指定消耗!')
if Mlogb.objects.filter(material_out__isnull=False, count_real=0, mlog=ins).exists(): if Mlogb.objects.filter(material_out__isnull=False, count_real=0, mlog=ins).exists():
raise ParseError('产出数量不能为0!') raise ParseError('产出数量不能为0!')
if ins.is_fix is False and ins.route:
process = ins.route.process
if Process.objects.filter(parent=process).exists() and not MlogUser.objects.filter(mlog=ins).exists():
raise ParseError('该日志子工序信息未完善!')
def bind_mlog(ticket: Ticket, transition, new_ticket_data: dict): def bind_mlog(ticket: Ticket, transition, new_ticket_data: dict):
ins = Mlog.objects.get(id=new_ticket_data['t_id']) ins = Mlog.objects.get(id=new_ticket_data['t_id'])

File diff suppressed because it is too large Load Diff

View File

@ -10,7 +10,6 @@ from rest_framework.exceptions import ParseError
from django.db import transaction from django.db import transaction
from apps.wpmw.filters import WprFilter from apps.wpmw.filters import WprFilter
from apps.utils.sql import query_one_dict from apps.utils.sql import query_one_dict
from django.db.models.expressions import RawSQL
class WprViewSet(CustomListModelMixin, RetrieveModelMixin, ComplexQueryMixin, CustomGenericViewSet): class WprViewSet(CustomListModelMixin, RetrieveModelMixin, ComplexQueryMixin, CustomGenericViewSet):
@ -18,7 +17,6 @@ class WprViewSet(CustomListModelMixin, RetrieveModelMixin, ComplexQueryMixin, Cu
动态产品 动态产品
""" """
perms_map = {"get": "*"} perms_map = {"get": "*"}
select_related_fields = ["wm", "mb", "material", "wm__material_ofrom"] select_related_fields = ["wm", "mb", "material", "wm__material_ofrom"]
prefetch_related_fields = ["defects"] prefetch_related_fields = ["defects"]
@ -29,10 +27,6 @@ class WprViewSet(CustomListModelMixin, RetrieveModelMixin, ComplexQueryMixin, Cu
ordering = ["number", "create_time"] ordering = ["number", "create_time"]
ordering_fields = ["number", "create_time", "update_time"] ordering_fields = ["number", "create_time", "update_time"]
search_fields = ["number", "material__name", "material__model", "material__specification", "number_out"] search_fields = ["number", "material__name", "material__model", "material__specification", "number_out"]
annotate_dict = {
"number_prefix": RawSQL("regexp_replace(number, '(\\d+)$', '')", []),
"number_suffix": RawSQL("COALESCE(NULLIF(regexp_replace(number, '.*?(\\d+)$', '\\1'), ''), '0')::bigint", []),
}
def filter_queryset(self, queryset): def filter_queryset(self, queryset):
qs = super().filter_queryset(queryset) qs = super().filter_queryset(queryset)
@ -42,7 +36,7 @@ class WprViewSet(CustomListModelMixin, RetrieveModelMixin, ComplexQueryMixin, Cu
qs.exclude(mb=None, wm=None) qs.exclude(mb=None, wm=None)
return qs return qs
@action(methods=["post"], detail=False, perms_map={"post": "*"}, serializer_class=WprNewSerializer) @action(methods=['post'], detail=False, perms_map={'post': '*'}, serializer_class=WprNewSerializer)
def new_number(self, request, *args, **kwargs): def new_number(self, request, *args, **kwargs):
"""获取新的编号""" """获取新的编号"""
data = request.data data = request.data
@ -53,17 +47,16 @@ class WprViewSet(CustomListModelMixin, RetrieveModelMixin, ComplexQueryMixin, Cu
wpr_last = wps_qs.order_by("number").last() wpr_last = wps_qs.order_by("number").last()
count = wps_qs.count() count = wps_qs.count()
last_number = wpr_last.number if count > 0 else None last_number = wpr_last.number if count > 0 else None
last_number_count = int(last_number.split("-")[-1].lstrip("0")) last_number_count = int(last_number.split("-")[-1].lstrip('0'))
mat = Material.objects.get(id=material_start) mat = Material.objects.get(id=material_start)
return Response({"count": count, "last_number": last_number, "material_model": mat.model, "last_number_count": last_number_count}) return Response({"count": count, "last_number": last_number, "material_model":mat.model, "last_number_count": last_number_count})
@action(methods=["get"], detail=False, perms_map={"get": "*"}) @action(methods=['get'], detail=False, perms_map={'get': '*'})
def number_out_last(self, request, *args, **kwargs): def number_out_last(self, request, *args, **kwargs):
"""获取最新的出库对外编号 """获取最新的出库对外编号
获取最新的出库对外编号(get请求传入prefix参数和with_unsubmit参数with_unsubmit默认为yes表示是否包含未出库的记录prefix为前缀'WPR-2023-0)""" 获取最新的出库对外编号(get请求传入prefix参数和with_unsubmit参数with_unsubmit默认为yes表示是否包含未出库的记录prefix为前缀'WPR-2023-0)"""
from apps.inm.models import MIOItemw from apps.inm.models import MIOItemw
prefix = request.query_params.get("prefix", None) prefix = request.query_params.get("prefix", None)
if not prefix: if not prefix:
raise ParseError("请传入前缀参数") raise ParseError("请传入前缀参数")
@ -74,7 +67,7 @@ class WprViewSet(CustomListModelMixin, RetrieveModelMixin, ComplexQueryMixin, Cu
SELECT id, number_out FROM wpmw_wpr SELECT id, number_out FROM wpmw_wpr
WHERE number_out ~ %s order by number_out desc limit 1 WHERE number_out ~ %s order by number_out desc limit 1
""" """
pattern = f"^{prefix}[0-9]+$" pattern = f'^{prefix}[0-9]+$'
number_outs = [] number_outs = []
wpr_qs_last = query_one_dict(query, [pattern]) wpr_qs_last = query_one_dict(query, [pattern])
if wpr_qs_last: if wpr_qs_last:
@ -94,7 +87,7 @@ class WprViewSet(CustomListModelMixin, RetrieveModelMixin, ComplexQueryMixin, Cu
if number_outs: if number_outs:
number_outs.sort() number_outs.sort()
number_out_last = number_outs[-1] number_out_last = number_outs[-1]
number_int_last = number_out_last.lstrip(prefix).lstrip("0") number_int_last = number_out_last.lstrip(prefix).lstrip('0')
try: try:
number_int_last = int(number_int_last) number_int_last = int(number_int_last)
except ValueError: except ValueError:
@ -102,8 +95,9 @@ class WprViewSet(CustomListModelMixin, RetrieveModelMixin, ComplexQueryMixin, Cu
return Response({"number_out_last": number_out_last, "number_out_int_last": int(number_int_last)}) return Response({"number_out_last": number_out_last, "number_out_int_last": int(number_int_last)})
else: else:
return Response({"number_out_last": None}) return Response({"number_out_last": None})
@action(methods=["post"], detail=False, perms_map={"post": "*"}, serializer_class=WproutListSerializer) @action(methods=['post'], detail=False, perms_map={'post': '*'}, serializer_class=WproutListSerializer)
@transaction.atomic @transaction.atomic
def assgin_number_out(self, request, *args, **kwargs): def assgin_number_out(self, request, *args, **kwargs):
"""分配出库对外编号 """分配出库对外编号
@ -114,7 +108,7 @@ class WprViewSet(CustomListModelMixin, RetrieveModelMixin, ComplexQueryMixin, Cu
vdata = sr.validated_data vdata = sr.validated_data
items = vdata["items"] items = vdata["items"]
number_outs = [i["number_out"] for i in items] number_outs = [i["number_out"] for i in items]
existing_numbers = Wpr.objects.filter(number_out__in=number_outs, number_out__isnull=False).values_list("number_out", flat=True) existing_numbers = Wpr.objects.filter(number_out__in=number_outs, number_out__isnull=False).values_list('number_out', flat=True)
if existing_numbers.exists(): if existing_numbers.exists():
used_numbers = list(existing_numbers) used_numbers = list(existing_numbers)
raise ParseError(f"以下对外编号已被使用: {used_numbers[0]}{len(used_numbers)}") raise ParseError(f"以下对外编号已被使用: {used_numbers[0]}{len(used_numbers)}")
@ -122,4 +116,4 @@ class WprViewSet(CustomListModelMixin, RetrieveModelMixin, ComplexQueryMixin, Cu
wpr = Wpr.objects.get(id=i["id"]) wpr = Wpr.objects.get(id=i["id"])
wpr.number_out = i["number_out"] wpr.number_out = i["number_out"]
wpr.save() wpr.save()
return Response() return Response()

View File

@ -1,15 +1,3 @@
## 2.8.2025101011
- feat: 新增功能
- Ptest val_xj可为空 [caoqianming]
- quick调用serializer时传入request [caoqianming]
- ofm-修改model [zty]
- Mroombooking添加字段 [caoqianming]
- base add_info_for_item 可复用list逻辑 [caoqianming]
- 添加wpr查询参数 [caoqianming]
- base cquery支持annotate [caoqianming]
- mroombooking 返回slots [caoqianming]
- fix: 问题修复
- p_create_after 自动创建mlogbw时关于exclude语句导致的查询错误 [caoqianming]
## 2.8.2025092816 ## 2.8.2025092816
- feat: 新增功能 - feat: 新增功能
- handover revert撤回时做校验 [caoqianming] - handover revert撤回时做校验 [caoqianming]

View File

@ -35,7 +35,7 @@ sys.path.insert(0, os.path.join(BASE_DIR, 'apps'))
ALLOWED_HOSTS = ['*'] ALLOWED_HOSTS = ['*']
SYS_NAME = '星途工厂综合管理系统' SYS_NAME = '星途工厂综合管理系统'
SYS_VERSION = '2.8.2025101011' SYS_VERSION = '2.8.2025092816'
X_FRAME_OPTIONS = 'SAMEORIGIN' X_FRAME_OPTIONS = 'SAMEORIGIN'
# Application definition # Application definition