合并
This commit is contained in:
zty 2025-04-23 16:49:00 +08:00
commit 8183073026
17 changed files with 153 additions and 21 deletions

2
.gitignore vendored
View File

@ -20,6 +20,8 @@ db.sqlite3
server/conf*.py
server/conf.ini
server/conf*.json
config/conf*.py
config/conf*.json
sh/*
temp/*
nohup.out

View File

@ -151,11 +151,20 @@ class MpLogxViewSet(CustomListModelMixin, CustomGenericViewSet):
serializer_class = MpLogxSerializer
filterset_fields = {
"timex": ["exact", "gte", "lte", "year", "month", "day"],
"mpoint": ["exact"],
"mpoint": ["exact", "in"],
"mpoint__ep_monitored": ["exact"]
}
ordering_fields = ["timex"]
ordering = ["-timex"]
@action(methods=["get"], detail=False, perms_map={"get": "*"})
def to_wide(self, request, *args, **kwargs):
"""转换为宽表
转换为宽表
"""
queryset = self.filter_queryset(self.get_queryset())
class MpointStatViewSet(BulkCreateModelMixin, BulkDestroyModelMixin, CustomListModelMixin, CustomGenericViewSet):
"""

View File

@ -1,9 +1,17 @@
from django.db import models
from apps.system.models import CommonADModel
from apps.system.models import CommonADModel, BaseModel
# Create your models here.
class Conversation(CommonADModel):
"""
TN: 对话
"""
title = models.CharField(max_length=200, default='新对话',verbose_name='对话标题')
title = models.CharField(max_length=200, default='新对话',verbose_name='对话标题')
class Message(BaseModel):
"""
TN: 消息
"""
conversation = models.ForeignKey(Conversation, on_delete=models.CASCADE, verbose_name='对话')
content = models.TextField(verbose_name='消息内容')
role = models.CharField("角色", max_length=10, help_text="system/user")

View File

@ -186,8 +186,8 @@ class RouteSerializer(CustomModelSerializer):
raise ParseError('未提供操作工序')
if process.parent is not None:
raise ParseError('操作工序不可为子工序')
if process.mtype == Process.PRO_DIV and attrs.get('div_number', 1) <= 1:
raise ParseError('切分数量必须大于1')
if process.mtype == Process.PRO_DIV and attrs.get('div_number', 1) < 1:
raise ParseError('切分数量必须大于等于1')
return super().validate(attrs)
@classmethod

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.12 on 2025-04-23 05:45
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('qm', '0049_alter_ptest_sample_number'),
]
operations = [
migrations.AlterField(
model_name='defect',
name='cate',
field=models.CharField(help_text="['尺寸', '外观', '内质', '性能']", max_length=50, verbose_name='分类'),
),
]

View File

@ -14,9 +14,10 @@ class Defect(CommonAModel):
DEFECT_OK = 10
DEFECT_OK_B = 20
DEFECT_NOTOK = 30
cate_list = ["尺寸", "外观", "内质", "性能"]
name = models.CharField(max_length=50, verbose_name="名称")
code = models.CharField(max_length=50, verbose_name="标识", null=True, blank=True)
cate = models.CharField(max_length=50, verbose_name="分类", choices=(("尺寸", "尺寸"), ("外观", "外观"), ("内质", "内质")))
cate = models.CharField(max_length=50, verbose_name="分类", help_text=str(cate_list))
okcate= models.PositiveSmallIntegerField(verbose_name="不合格分类",
choices=((DEFECT_OK, "合格"), (DEFECT_OK_B, "合格B类"), (DEFECT_NOTOK, "不合格")),
default=DEFECT_NOTOK)

View File

@ -15,6 +15,12 @@ class DefectSerializer(CustomModelSerializer):
model = Defect
fields = '__all__'
read_only_fields = EXCLUDE_FIELDS
def validate(self, attrs):
cate = attrs["cate"]
if cate not in Defect.cate_list:
raise ParseError("缺陷类别错误")
return attrs
# def create(self, validated_data):
# code = validated_data["code"]

View File

@ -0,0 +1,20 @@
# Generated by Django 3.2.12 on 2025-04-22 07:45
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('mtm', '0056_mgroup_batch_append_code'),
('wpm', '0112_auto_20250422_1430'),
]
operations = [
migrations.AddField(
model_name='mlog',
name='team',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mtm.team', verbose_name='班组'),
),
]

View File

@ -252,6 +252,7 @@ class Mlog(CommonADModel):
count_n_qt = models.DecimalField('其他', default=0, max_digits=11, decimal_places=1)
handle_date = models.DateField('操作日期', null=True, blank=True)
team = models.ForeignKey(Team, verbose_name='班组', on_delete=models.SET_NULL, null=True, blank=True)
handle_user = models.ForeignKey(
User, verbose_name='操作人', on_delete=models.CASCADE, related_name='mlog_handle_user', null=True, blank=True) # 成型人
handle_user_2 = models.ForeignKey(
@ -613,7 +614,7 @@ class BatchSt(BaseModel):
class Meta:
unique_together = [("batch", "version")]
@classmethod
def g_create(cls, batch:str, mio=None, handover=None, mlog=None, material_start=None):
"""
@ -622,13 +623,14 @@ class BatchSt(BaseModel):
if mio is None and handover is None and mlog is None:
return cls.objects.get_or_create(batch=batch)
else:
latest_version = 0
version = 1
# 带有来源的批次获取,需检查批次号是否可用
if cls.objects.filter(batch=batch, version=0).exists():
if cls.objects.filter(batch=batch, version=1).exists():
latest_version = BatchSt.objects.filter(batch=batch).aggregate(Max("version"))["version__max"]
version = latest_version + 1
if mio is None and handover is None and mlog is None:
raise ParseError("mio or handover or mlog must be provided")
ins = cls.objects.create(batch=batch, mio=mio, handover=handover, mlog=mlog, material_start=material_start, version=latest_version+1)
ins = cls.objects.create(batch=batch, mio=mio, handover=handover, mlog=mlog, material_start=material_start, version=version)
return ins, True
@classmethod

View File

@ -234,6 +234,7 @@ class MlogbSerializer(CustomModelSerializer):
class MlogListSerializer(CustomModelSerializer):
team_name = serializers.CharField(source="team.name", read_only=True)
mstate_json = serializers.JSONField(source='mgroup.process.mstate_json', read_only=True)
supplier_name = serializers.CharField(source='supplier.name', read_only=True)
# routepack_name = serializers.CharField(source='route.routepack.name', read_only=True)
@ -291,6 +292,7 @@ class MlogbDetailSerializer(CustomModelSerializer):
fields = '__all__'
class MlogSerializer(CustomModelSerializer):
team_name = serializers.CharField(source="team.name", read_only=True)
mstate_json = serializers.JSONField(source='mgroup.process.mstate_json', read_only=True)
supplier_name = serializers.CharField(source='supplier.name', read_only=True)
# routepack_name = serializers.CharField(source='route.routepack.name', read_only=True)
@ -748,7 +750,7 @@ class MlogbInUpdateSerializer(CustomModelSerializer):
mlogbdefect = MlogbDefectSerializer(many=True, required=False)
class Meta:
model = Mlogb
fields = ['id', 'count_use', 'count_pn_jgqbl', 'note', 'mlogbdefect']
fields = ['id', 'count_use', 'count_pn_jgqbl', 'note', 'mlogbdefect', "need_inout"]
def validate(self, attrs):
if attrs["count_use"] < 0 or attrs["count_pn_jgqbl"] < 0:
@ -858,6 +860,16 @@ class MlogbOutUpdateSerializer(CustomModelSerializer):
mlogbdefect = validated_data.pop("mlogbdefect", None)
with transaction.atomic():
ins:Mlogb = super().update(instance, validated_data)
if ins.need_inout is False:
if ins.mlogb_from:
if Mlogb.objects.filter(mlog=ins.mlog, material_out__isnull=False, mlogb_from=ins.mlogb_from).count() == 1:
ins_from =Mlogb.objects.filter(mlog=ins.mlog, material_out__isnull=False, mlogb_from=ins.mlogb_from).first()
ins_from.need_inout = False
ins_from.save(update_fields=["need_inout"])
else:
raise ParseError("对应消耗的产出有多个, 需手动指定消耗是否出库")
else:
raise ParseError("该产出需入库!")
if mlogbdefect is not None and ins.material_out.tracking == Material.MA_TRACKING_BATCH:
MlogbDefect.objects.filter(mlogb=ins).delete()
mlogb_defect_objects = [

View File

@ -190,6 +190,7 @@ def mlog_submit(mlog: Mlog, user: User, now: Union[datetime.datetime, None]):
m_ins_list = []
m_ins = Mlogb.objects.filter(mlog=mlog, material_in__isnull=False)
if m_ins.exists():
m_ins = m_ins.filter(need_inout=True)
m_ins_list = [(mi.material_in, mi.batch if mi.batch else mi.batch, mi.count_use, None, mi) for mi in m_ins.all()]
for item in m_ins:
mbd_qs = MlogbDefect.get_defect_qs_from_mlogb(item)
@ -260,7 +261,8 @@ def mlog_submit(mlog: Mlog, user: User, now: Union[datetime.datetime, None]):
mlogb_out_qs = Mlogb.objects.filter(mlog=mlog, material_out__isnull=False)
stored_notok = need_store_notok
stored_mgroup = need_store_notok
if mlogb_out_qs.exists():
if mlogb_out_qs.exists():
mlogb_out_qs = mlogb_out_qs.filter(need_inout=True)
m_outs_list = [(mo.material_out, mo.batch if mo.batch else mlog.batch, mo.count_ok_full if mo.count_ok_full is not None else mo.count_ok, mlog.count_real_eweight, None, mo) for mo in mlogb_out_qs.all()]
if need_store_notok:
for item in mlogb_out_qs:
@ -402,6 +404,7 @@ def mlog_revert(mlog: Mlog, user: User, now: Union[datetime.datetime, None]):
# 需要考虑不合格品退回的情况
mlogb_out_qs = Mlogb.objects.filter(mlog=mlog, material_out__isnull=False)
if mlogb_out_qs.exists():
mlogb_out_qs = mlogb_out_qs.filter(need_inout=True)
m_outs_list = [
(mo.material_out, mo.batch if mo.batch else mlog.batch, mo.count_ok_full if mo.count_ok_full is not None else mo.count_ok, mlog.count_real_eweight, None, mo)
for mo in mlogb_out_qs.all()]
@ -497,6 +500,7 @@ def mlog_revert(mlog: Mlog, user: User, now: Union[datetime.datetime, None]):
into_wm_mgroup = process.into_wm_mgroup
m_ins = Mlogb.objects.filter(mlog=mlog, material_in__isnull=False)
if m_ins.exists():
m_ins = m_ins.filter(need_inout=True)
for mi in m_ins.all():
m_ins_list.append((mi.material_in, mi.batch, mi.count_use, None, mi))
for item in m_ins:

View File

@ -5,7 +5,7 @@ from apps.inm.models import MIOItem
from apps.qm.models import FtestWork
from django.utils import timezone
from datetime import datetime
from server.conf import BASE_PROJECT_CODE
from django.conf import settings
import json
from apps.utils.tools import MyJSONEncoder
import decimal
@ -17,6 +17,7 @@ def get_alldata_with_batch_and_store(batch: str):
获取某个批次的整体生产数据并保存
"""
need_update = False
BASE_PROJECT_CODE = getattr(settings, "BASE_PROJECT_CODE", None)
if BASE_PROJECT_CODE == "gzerp":
need_update = True
last_time, data = get_alldata_with_batch(batch)

View File

@ -174,13 +174,13 @@ class MlogViewSet(CustomModelViewSet):
select_related_fields = ['create_by', 'update_by', 'mtask', 'mtaskb',
'handle_user', 'handle_user_2', 'equipment', 'mgroup__belong_dept',
'equipment_2', 'material_in', 'material_out', 'route__routepack',
'supplier', 'ticket', 'mgroup__process', 'test_user', 'handle_leader', 'test_user']
'supplier', 'ticket', 'mgroup__process', 'test_user', 'handle_leader', 'test_user', 'team']
prefetch_related_fields = ['handle_users',
'material_outs', 'b_mlog', 'equipments']
filterset_class = MlogFilter
search_fields = ['material_in__name',
'material_in__number', 'material_in__specification', 'batch', 'material_in__model',
'material_out__name', 'material_out__number', 'material_out__specification', 'material_out__model',]
'material_out__name', 'material_out__number', 'material_out__specification', 'material_out__model']
def add_info_for_item(self, data):
if data.get("oinfo_json", {}):
@ -624,8 +624,11 @@ class MlogbInViewSet(CreateModelMixin, UpdateModelMixin, DestroyModelMixin, Cust
{"count_real": d_count_real, "count_ok": d_count_ok}))
for mlogbwin in Mlogbw.objects.filter(mlogb=mlogbin).order_by("number"):
wpr_ = mlogbwin.wpr
for i in range(div_number):
Mlogbw.objects.get_or_create(mlogb=mlogbout, number=f'{wpr_.number}-{i+1}', defaults={"mlogbw_from": mlogbwin})
if div_number == 1:
Mlogbw.objects.get_or_create(wpr=wpr_, mlogb=mlogbout, defaults={"number": wpr_.number, "mlogbw_from": mlogbwin})
else:
for i in range(div_number):
Mlogbw.objects.get_or_create(mlogb=mlogbout, number=f'{wpr_.number}-{i+1}', defaults={"mlogbw_from": mlogbwin})
elif material_in.tracking == Material.MA_TRACKING_BATCH and material_out.tracking == Material.MA_TRACKING_BATCH:
d_count_real = mlogbin.count_use * div_number
d_count_ok = d_count_real

View File

@ -1,3 +1,37 @@
## 2.6.2025042311
- feat: 新增功能
- 切分工序切分数量支持1 [caoqianming]
- mlog增加team字段 [caoqianming]
- mlog的need_inout逻辑 [caoqianming]
- 批次关系链时创建新批次支持使用已有批次号 [caoqianming]
- mlogb添加test_user/need_inout字段用于处理抽检逻辑 [caoqianming]
- 工艺步骤返回组合而成的name [caoqianming]
- 校验只有合并时才能提供新批次号 [caoqianming]
- 交接查询可查询子批次号 [caoqianming]
- 日志完善负值校验 [caoqianming]
- 批号追加工段标识 [caoqianming]
- base增加PositiveDecimalField [caoqianming]
- 改版交接需要触发统计数量 [caoqianming]
- 物料统计数量接口 [caoqianming]
- update_material_count时更新组合件数量 [caoqianming]
- 采购和其他入库可入已有批次 [caoqianming]
- 交接需要校验物料是否一致 [caoqianming]
- 返工可选择不合格品/根据工艺路线决定返工后是合格不合格还是返修完成 [caoqianming]
- 检验项和缺陷项删除的时候同步删除qct [caoqianming]
- fmlog添加工艺步骤字段及相应返回数据 [caoqianming]
- fix: 问题修复
- mlog的need_inout逻辑 [caoqianming]
- count_working获取逻辑优化 [caoqianming]
- mlogbin解决负值校验存在的bug [caoqianming]
- 日志和交接记录操作数正值校验 [caoqianming]
- inm校验非正数 [caoqianming]
- 其他入库时batchst.g_create传参错误 [caoqianming]
- : 交接记录提交时校验count>0 [caoqianming]
- 正常交接后的物料状态不变 [caoqianming]
- 存在fmlog时将route带给mlog并进行物料校验 [caoqianming]
- do_in保证production_dept赋值 [caoqianming]
- 订单检索条件错误 [caoqianming]
- fmlog填写mtask或route即可 [caoqianming]
## 2.6.2025041613
- feat: 新增功能
- 添加count_json_from字段及相应逻辑 [caoqianming]

12
config/e.conf.py Normal file
View File

@ -0,0 +1,12 @@
SECRET_KEY = 'xx'
DEBUG = False
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'xx',
'USER': 'postgres',
'PASSWORD': 'xx',
'HOST': 'xx',
'PORT': '5432',
}
}

View File

@ -1,5 +1,5 @@
import os
from . import conf
from config import conf
from celery import Celery
from celery.app.control import Control, Inspect

View File

@ -14,7 +14,7 @@ from datetime import datetime, timedelta
import os
import json
import sys
from .conf import *
from config.conf import *
from django.core.cache import cache
import logging
@ -35,7 +35,7 @@ sys.path.insert(0, os.path.join(BASE_DIR, 'apps'))
ALLOWED_HOSTS = ['*']
SYS_NAME = '星途工厂综合管理系统'
SYS_VERSION = '2.6.2025041613'
SYS_VERSION = '2.6.2025042311'
X_FRAME_OPTIONS = 'SAMEORIGIN'
# Application definition
@ -369,7 +369,7 @@ LOGGING = {
}
##### 加载客户可自定义配置并提供操作方法 #####
SYS_JSON_PATH = os.path.join(BASE_DIR, 'server/conf.json')
SYS_JSON_PATH = os.path.join(BASE_DIR, 'config/conf.json')
def get_sysconfig(key='', default='raise_error', reload=False):
"""获取系统配置可指定key字符串