Compare commits

..

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

33 changed files with 488 additions and 950 deletions

View File

@ -62,7 +62,7 @@ class Mpoint(CommonBModel):
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)
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
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:
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:
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:
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)
else:
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)

View File

@ -37,9 +37,7 @@ class MioFilter(filters.FilterSet):
"item_mio__test_user": ["isnull"],
"item_mio__w_mioitem__number": ["exact"],
"mgroup": ["exact"],
"item_mio__batch": ["exact"],
"inout_date": ["gte", "lte", "exact"],
"belong_dept": ["exact"]
"item_mio__batch": ["exact"]
}
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 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
do_user = mio.do_user
material:Material = item.material
if material.into_wm is False: # 用于混料的原料不与车间库存交互, 这个是配置项目
return
# 获取defect
defect:Defect = None
@ -96,24 +98,22 @@ def do_out(item: MIOItem, is_reverse: bool = False):
else:
mb.save()
if material.into_wm:
# 领到车间库存(或工段)
wm, new_create = WMaterial.objects.get_or_create(
batch=xbatch, material=xmaterial,
belong_dept=belong_dept, mgroup=mgroup,
state=WMaterial.WM_OK, defect=defect)
if new_create:
wm.create_by = do_user
wm.batch_ofrom = mb.batch if mb else None
wm.material_ofrom = mb.material if mb else None
wm.count = wm.count + item.count
wm.update_by = do_user
wm.save()
# 领到车间库存(或工段)
wm, new_create = WMaterial.objects.get_or_create(
batch=xbatch, material=xmaterial,
belong_dept=belong_dept, mgroup=mgroup,
state=WMaterial.WM_OK, defect=defect)
if new_create:
wm.create_by = do_user
wm.batch_ofrom = mb.batch if mb else None
wm.material_ofrom = mb.material if mb else None
wm.count = wm.count + item.count
wm.update_by = do_user
wm.save()
# 开始变动wpr
if xmaterial.tracking == Material.MA_TRACKING_SINGLE:
if material.into_wm is False:
raise ParseError("追踪单个物料不支持不进行车间库存的操作")
mioitemws = MIOItemw.objects.filter(mioitem=item)
if mioitemws.count() != item.count:
raise ParseError("出入库与明细数量不一致,操作失败")
@ -141,7 +141,8 @@ def do_in(item: MIOItem):
mgroup = mio.mgroup
do_user = mio.do_user
material = item.material
if material.into_wm is False: # 根据配置不进行入车间库存的处理
return
action_list = []
mias = MIOItemA.objects.filter(mioitem=item)
is_zhj = False # 是否组合件入仓库
@ -176,39 +177,38 @@ def do_in(item: MIOItem):
raise ParseError("存在非正数!")
xbatchs.append(xbatch)
if material.into_wm:
wm_qs = WMaterial.objects.filter(
batch=xbatch,
material=xmaterial,
belong_dept=belong_dept,
mgroup=mgroup,
defect=defect,
state=WMaterial.WM_OK)
count_x = wm_qs.count()
if count_x == 1:
wm = wm_qs.first()
elif count_x == 0:
raise ParseError(
f'{str(xmaterial)}-{xbatch}-批次库存不存在!')
else:
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}车间物料不属于同一车间')
wm_qs = WMaterial.objects.filter(
batch=xbatch,
material=xmaterial,
belong_dept=belong_dept,
mgroup=mgroup,
defect=defect,
state=WMaterial.WM_OK)
count_x = wm_qs.count()
if count_x == 1:
wm = wm_qs.first()
elif count_x == 0:
raise ParseError(
f'{str(xmaterial)}-{xbatch}-批次库存不存在!')
else:
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
if not is_zhj:
mb, _ = MaterialBatch.objects.get_or_create(
@ -231,8 +231,6 @@ def do_in(item: MIOItem):
# 开始变动wpr
if xmaterial.tracking == Material.MA_TRACKING_SINGLE:
if material.into_wm is False:
raise ParseError("追踪单个物料不支持不进行车间库存的操作")
mioitemws = MIOItemw.objects.filter(mioitem=item)
if mioitemws.count() != item.count:
raise ParseError("出入库与明细数量不一致,操作失败")

View File

@ -39,7 +39,7 @@ class MaterialViewSet(CustomModelViewSet):
ordering = ['name', 'model', 'specification',
'type', 'process', 'process__sort', 'sort', 'id', 'number']
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):
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

@ -3,7 +3,6 @@ from apps.utils.models import CommonADModel, BaseModel, CommonBDModel
from apps.system.models import User
from django.core.validators import RegexValidator
from datetime import datetime
from rest_framework.exceptions import ParseError
# Create your models here.
@ -26,15 +25,11 @@ class Mroom(CommonADModel):
location = models.CharField('位置', max_length=100)
capacity = models.PositiveIntegerField('容纳人数')
class MroomBooking(CommonBDModel):
class MroomBooking(CommonADModel):
"""TN: 会议室预定信息"""
# belong_dept 是预定部门
title = models.CharField('会议主题', max_length=100)
ticket = models.ForeignKey('wf.ticket', verbose_name='关联会议室',
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):
@ -44,6 +39,9 @@ class MroomSlot(BaseModel):
mdate = models.DateField('会议日期', db_index=True)
slot = models.PositiveIntegerField('时段', help_text='0-47')
is_inuse = models.BooleanField('是否占用', default=True)
class Meta:
unique_together = ('mroom', 'mdate', 'slot', 'is_inuse')
# 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)
def save(self, *args, **kwargs):
if self.end_km:
if self.start_km <= self.end_km:
self.actual_km = self.end_km - self.start_km
else:
raise ParseError('归还公里数不能小于出发公里数')
self.actual_km = self.end_km - self.start_km
else:
self.actual_km = 0
return super().save(*args, **kwargs)
@ -119,12 +114,12 @@ class BorrowRecord(CommonBDModel):
class Publicity(CommonBDModel):
"""TN: 公示栏"""
number = models.CharField('记录编号', max_length=50, blank=True, null=True)
number = models.CharField('记录编号', max_length=50)
title = models.CharField('送审稿件标题', max_length=100)
participants = models.CharField('所有撰稿人', max_length=50)
pub_dept = models.CharField('部室/研究院', null=True, blank=True, max_length=50)
pfile = models.CharField('稿件路径', null=True, blank=True, max_length=100)
level = models.JSONField('涉密等级', default=list, help_text=['重要', '一般', '非涉密'])
level = models.JSONField('用途', default=list, help_text=['重要', '一般', '非涉密'])
content = models.JSONField('稿件内容涉及', default=list, help_text=[
"武器装备科研生产综合事项",
"其它"
@ -134,58 +129,59 @@ class Publicity(CommonBDModel):
channel = models.JSONField('发布渠道', default=list, help_text=['互联网', '信息平台', '官微', '公开发行物', '其它'])
other_channel = 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=['内容不涉及国家秘密和商业秘密,申请公开', '内容涉及国家秘密,申请按涉密渠道发布'], null=True,blank=True)
dept_opinion = models.JSONField('部门负责人意见', default=list, help_text=['同意', '不同意'], null=True, blank=True)
secret_period = models.CharField('秘密期限', max_length=50, blank=True, null=True)
review = models.JSONField('第一撰稿人自审', default=list, help_text=['内容不涉及国家秘密和商业秘密,申请公开', '内容涉及国家秘密,申请按涉密渠道发布'])
dept_opinion = models.JSONField('部门负责人意见', default=list, help_text=['同意', '不同意'])
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='关联工单',
on_delete=models.SET_NULL, related_name='publicity_ticket', null=True, blank=True, db_constraint=False)
# 记录编号自动生成
def save(self, *args, **kwargs):
if not self.number:
last_number = self.__class__.objects.filter(number__startswith=f"GXKG-{datetime.now().year}-").order_by('-number').first()
if last_number:
try:
last_num = int(last_number.number.split('-')[-1])
except ValueError:
last_num = 0
else:
last_num =0
# 格式化编号,带补零
self.number = f"GXKG-{datetime.now().year}-{last_num+1:02d}"
with transaction.atomic():
# 加行锁,防止并发取到相同 last_number
last_number = self.__class__.objects.select_for_update(skip_locked=True).order_by('-id').first() # skip_locked 锁定行,避免并发冲突
if last_number:
try:
last_num = int(last_number.number.split('-')[-1])
except ValueError:
last_num = 0
new_num= last_num + 1
else:
new_num = 1
# 格式化编号,带补零
self.number = f"(GXKG-{datetime.now().year}-{new_num:02d})"
super().save(*args, **kwargs)
class PatentInfo(CommonBDModel):
"""TN: 专利申密审批表单样式"""
PATENT_TYPE_CHOICES = (
('invention', '发明专利'),
('utility', '实用新型专利'),
('design', '外观设计专利'),
)
APPLY_AREAS = (
('Domestic', '国内申请'),
('Foreign', '国外申请'),
(' 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)
ticket = models.ForeignKey('wf.ticket', verbose_name='关联工单',
on_delete=models.SET_NULL, related_name='patentInfo_ticket', null=True, blank=True, db_constraint=False)
# class PatentInfo(CommonBDModel):
# """TN: 专利申密审批表单样式"""
# PATENT_TYPE_CHOICES = (
# ('invention', '发明专利'),
# ('utility', '实用新型专利'),
# ('design', '外观设计专利'),
# )
# APPLY_AREAS = (
# ('Domestic', '国内申请'),
# ('Foreign', '国外申请'),
# (' 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)
# class PaperOfm(CommonADModel):
# """TN: 论文申密审批表单"""

View File

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

View File

@ -1,7 +1,7 @@
from apps.wf.models import Ticket
# 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
@ -31,7 +31,6 @@ def mroombooking_reject(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.actual_return_date = None
ticket_data = ticket.ticket_data
ticket_data.update({
't_model': 'LendingSeal',
@ -43,19 +42,6 @@ def bind_lendingseal(ticket: Ticket, transition, new_ticket_data: dict):
if ins.ticket is None:
ins.ticket = ticket
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):
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_id': ins.id,
})
ins.actual_km = None
ins.end_time = None
ticket.ticket_data = ticket_data
ticket.create_by = ins.create_by
ticket.save()
@ -73,16 +57,6 @@ def bind_vehicle(ticket: Ticket, transition, new_ticket_data: dict):
ins.ticket = ticket
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):
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_id': ins.id,
})
ins.return_date = 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 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):
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_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.create_by = ins.create_by
ticket.save()

View File

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

View File

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

View File

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

View File

@ -85,4 +85,3 @@ class ComplexSerializer(serializers.Serializer):
ordering = serializers.CharField(required=False)
querys = serializers.ListField(child=QuerySerializer(
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(
'内容模板', 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_path2 = models.TextField('前端自定义页面路径2', null=True, blank=True)
class Meta:
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
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)
handle_user = models.ForeignKey(User, 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)
handle_date = models.DateField('操作日期')
@ -527,7 +524,7 @@ class Mlogbw(BaseModel):
mlogb.count_real = count
count_notok = 0
count_notok_full = 0
# 个追踪的合格b类不分这里会导致count_ok_full与count_ok一样了暂时不做处理
# 个追踪的合格b类不分
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'))
defect_ids = tqs.values_list("ftest__defect_main", flat=True)

View File

@ -355,12 +355,12 @@ class MlogSerializer(CustomModelSerializer):
model = Mlog
fields = '__all__'
read_only_fields = EXCLUDE_FIELDS + \
['submit_time', 'submit_user', 'material_outs']
['submit_time', 'submit_user', 'material_outs', "handle_date", "shift"]
extra_kwargs = {
"batch": {"required": True},
"shift": {"required": False},
"material_out": {"required": True},
"work_start_time": {"required": False}
"work_start_time": {"required": True}
}
def create(self, validated_data):
@ -634,20 +634,14 @@ class MlogSerializer(CustomModelSerializer):
# 时间
mgroup:Mgroup = attrs['mgroup']
work_start_time:datetime = attrs.get('work_start_time', None)
if 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('缺少生产日期')
work_start_time:datetime = attrs['work_start_time']
handle_date, attrs["shift"] = mgroup.get_shift(work_start_time)
if mtask and mtask.start_date == mtask.end_date:
if attrs['handle_date'] != mtask.end_date:
if work_start_time:
raise ParseError('任务日期与生产日期不一致')
else:
attrs['handle_date'] = mtask.end_date
attrs['handle_date'] = mtask.end_date
if attrs['handle_date'] != handle_date:
raise ParseError('任务日期与生产日期不一致')
else:
attrs['handle_date'] = handle_date
handle_user = attrs.get('handle_user', None)
if handle_user is None and hasattr(self, "request"):
@ -1492,17 +1486,10 @@ class MlogUserSerializer(CustomModelSerializer):
class Meta:
model = MlogUser
fields = "__all__"
read_only_fields = EXCLUDE_FIELDS_BASE + ["shift", "handle_date"]
extra_kwargs = {
"work_start_time": {"required": True}
}
read_only_fields = EXCLUDE_FIELDS_BASE
def create(self, validated_data):
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:
raise ParseError("该日志已提交")
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.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.wf.models import Ticket
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('操作开始时间不能晚于当前时间')
if mlog.work_start_time and mlog.work_end_time and mlog.work_end_time < mlog.work_start_time:
raise ParseError('操作结束时间不能早于操作开始时间')
@ -385,7 +385,7 @@ def mlog_submit(mlog: Mlog, user: User, now: Union[datetime.datetime, None]):
mlog.submit_user = user
mlog.stored_notok = stored_notok
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.save()
@ -1026,10 +1026,6 @@ def mlog_submit_validate(ins: Mlog):
raise ParseError('该日志未指定消耗!')
if Mlogb.objects.filter(material_out__isnull=False, count_real=0, mlog=ins).exists():
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):
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 apps.wpmw.filters import WprFilter
from apps.utils.sql import query_one_dict
from django.db.models.expressions import RawSQL
class WprViewSet(CustomListModelMixin, RetrieveModelMixin, ComplexQueryMixin, CustomGenericViewSet):
@ -18,7 +17,6 @@ class WprViewSet(CustomListModelMixin, RetrieveModelMixin, ComplexQueryMixin, Cu
动态产品
"""
perms_map = {"get": "*"}
select_related_fields = ["wm", "mb", "material", "wm__material_ofrom"]
prefetch_related_fields = ["defects"]
@ -29,10 +27,6 @@ class WprViewSet(CustomListModelMixin, RetrieveModelMixin, ComplexQueryMixin, Cu
ordering = ["number", "create_time"]
ordering_fields = ["number", "create_time", "update_time"]
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):
qs = super().filter_queryset(queryset)
@ -42,7 +36,7 @@ class WprViewSet(CustomListModelMixin, RetrieveModelMixin, ComplexQueryMixin, Cu
qs.exclude(mb=None, wm=None)
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):
"""获取新的编号"""
data = request.data
@ -53,17 +47,16 @@ class WprViewSet(CustomListModelMixin, RetrieveModelMixin, ComplexQueryMixin, Cu
wpr_last = wps_qs.order_by("number").last()
count = wps_qs.count()
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)
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": "*"})
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': '*'})
def number_out_last(self, request, *args, **kwargs):
"""获取最新的出库对外编号
获取最新的出库对外编号(get请求传入prefix参数和with_unsubmit参数with_unsubmit默认为yes表示是否包含未出库的记录prefix为前缀'WPR-2023-0)"""
from apps.inm.models import MIOItemw
prefix = request.query_params.get("prefix", None)
if not prefix:
raise ParseError("请传入前缀参数")
@ -74,7 +67,7 @@ class WprViewSet(CustomListModelMixin, RetrieveModelMixin, ComplexQueryMixin, Cu
SELECT id, number_out FROM wpmw_wpr
WHERE number_out ~ %s order by number_out desc limit 1
"""
pattern = f"^{prefix}[0-9]+$"
pattern = f'^{prefix}[0-9]+$'
number_outs = []
wpr_qs_last = query_one_dict(query, [pattern])
if wpr_qs_last:
@ -94,7 +87,7 @@ class WprViewSet(CustomListModelMixin, RetrieveModelMixin, ComplexQueryMixin, Cu
if number_outs:
number_outs.sort()
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:
number_int_last = int(number_int_last)
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)})
else:
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
def assgin_number_out(self, request, *args, **kwargs):
"""分配出库对外编号
@ -114,7 +108,7 @@ class WprViewSet(CustomListModelMixin, RetrieveModelMixin, ComplexQueryMixin, Cu
vdata = sr.validated_data
items = vdata["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():
used_numbers = list(existing_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.number_out = i["number_out"]
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
- feat: 新增功能
- handover revert撤回时做校验 [caoqianming]

View File

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