This commit is contained in:
TianyangZhang 2025-12-01 10:08:30 +08:00
commit 36df498e08
14 changed files with 216 additions and 33 deletions

View File

@ -2,13 +2,11 @@ from django_filters import rest_framework as filters
from apps.mtm.models import Goal, Material, Route, RoutePack
from django.db.models.expressions import F
from rest_framework.exceptions import ParseError
from django.db.models import Sum, Q, Value, F, ExpressionWrapper, DecimalField
from django.db.models.functions import Coalesce
class MaterialFilter(filters.FilterSet):
tag = filters.CharFilter(method='filter_tag', label="low_inm:库存不足")
count__gt = filters.NumberFilter(field_name='count', lookup_expr='gt')
count_mb__gt = filters.NumberFilter(field_name='count_mb', lookup_expr='gt')
count_wm__gt = filters.NumberFilter(field_name='count_wm', lookup_expr='gt')
class Meta:
model = Material
@ -30,7 +28,7 @@ class MaterialFilter(filters.FilterSet):
def filter_tag(self, queryset, name, value):
if value == 'low_inm':
queryset = queryset.exclude(count_safe=None).exclude(count_safe__lte=0).filter(
queryset = Material.annotate_count(queryset.exclude(count_safe=None).exclude(count_safe__lte=0)).filter(
count__lte=F('count_safe'))
return queryset

View File

@ -6,6 +6,8 @@ from collections import defaultdict, deque
from django.db.models import Q
from datetime import datetime, timedelta
from django.utils import timezone
from django.db.models import Sum, Q, Value, F, ExpressionWrapper, DecimalField
from django.db.models.functions import Coalesce
class Process(CommonBModel):
"""
@ -123,6 +125,25 @@ class Material(CommonAModel):
def fname(self):
return f'{self.name}|{self.specification if self.specification else ""}|{self.model if self.model else ""}|{self.process.name if self.process else ""}'
@staticmethod
def annotate_count(qs):
return qs.annotate(
count_mb=Coalesce(
Sum('mb_m__count', filter=Q(mb_m__state=10, mb_m__count__gt=0)),
Value(0),
output_field=DecimalField()
),
count_wm=Coalesce(
Sum('wm_m__count', filter=Q(wm_m__state=10, wm_m__count__gt=0)),
Value(0),
output_field=DecimalField()
)
).annotate(
count=ExpressionWrapper(
F('count_wm') + F('count_mb'),
output_field=DecimalField()
)
)
class Shift(CommonBModel):
"""TN:班次

View File

@ -38,13 +38,15 @@ class MaterialSerializer(CustomModelSerializer):
count = serializers.SerializerMethodField()
def get_count_mb(self, obj):
return getattr(obj, 'count_mb', 0)
return getattr(obj, 'count_mb', None)
def get_count_wm(self, obj):
return getattr(obj, 'count_wm', 0)
return getattr(obj, 'count_wm', None)
def get_count(self, obj):
return getattr(obj, 'count_mb', 0) + getattr(obj, 'count_wm', 0)
if hasattr(obj, 'count_mb') and hasattr(obj, 'count_wm'):
return getattr(obj, 'count_mb', 0) + getattr(obj, 'count_wm', 0)
return None
class Meta:
model = Material

View File

@ -44,24 +44,8 @@ class MaterialViewSet(CustomModelViewSet):
def get_queryset(self):
qs = super().get_queryset()
if self.action in ["list", "retrieve"]:
return qs.annotate(
count_mb=Coalesce(
Sum('mb_m__count', filter=Q(mb_m__state=10)),
Value(0),
output_field=DecimalField()
),
count_wm=Coalesce(
Sum('wm_m__count', filter=Q(wm_m__state=10)),
Value(0),
output_field=DecimalField()
)
).annotate(
count=ExpressionWrapper(
F('count_wm') + F('count_mb'),
output_field=DecimalField()
)
)
if self.action in ["list", "retrieve"] and self.request.query_params.get('wiith_count', None) == 'yes':
return Material.annotate_count(qs)
return qs
def perform_destroy(self, instance):

View File

@ -1,7 +1,7 @@
from rest_framework import serializers
from apps.utils.serializers import CustomModelSerializer
from apps.utils.constants import EXCLUDE_FIELDS_DEPT, EXCLUDE_FIELDS_BASE, EXCLUDE_FIELDS
from rest_framework.exceptions import ValidationError
from rest_framework.exceptions import ValidationError, ParseError
from apps.pum.models import Supplier, PuPlan, PuPlanItem, PuOrder, PuOrderItem, SupplierAudit
from apps.mtm.serializers import MaterialSerializer, MaterialSimpleSerializer
@ -152,3 +152,9 @@ class SupplierAuditSerializer(CustomModelSerializer):
model = SupplierAudit
fields = "__all__"
read_only_fields = EXCLUDE_FIELDS_BASE + ['ticket']
def create(self, validated_data):
name = validated_data["name"]
if Supplier.objects.filter(name=name).exists():
raise ParseError('供应商名称已存在')
return super().create(validated_data)

View File

@ -12,6 +12,7 @@ from rest_framework.response import Response
from django.utils import timezone
from apps.pum.services import PumService
from apps.wf.mixins import TicketMixin
from apps.wf.models import Ticket
# Create your views here.
@ -42,6 +43,13 @@ class SupplierAuditViewSet(TicketMixin, CustomModelViewSet):
search_fields = ['name', 'material_name', 'material_cate']
workflow_key = "wf_supplieraudit"
@staticmethod
def add_supplier(ticket: Ticket, transition, new_ticket_data: dict):
supplieraudit = SupplierAudit.objects.get(ticket=ticket)
if Supplier.objects.filter(name=supplieraudit.name).exists():
raise ParseError('供应商名称已存在')
Supplier.objects.create(name=supplieraudit.name)
class PuPlanViewSet(CustomModelViewSet):
"""
list: 采购计划

View File

@ -0,0 +1,19 @@
# Generated by Django 3.2.12 on 2025-11-26 01:53
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('qm', '0054_alter_ptest_val_xj'),
]
operations = [
migrations.AlterField(
model_name='ftestitem',
name='ftest',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='items_ftest', to='qm.ftest', verbose_name='关联检验'),
),
]

View File

@ -94,7 +94,7 @@ class TicketSimpleSerializer(CustomModelSerializer):
class TicketCreateSerializer(CustomModelSerializer):
transition = serializers.PrimaryKeyRelatedField(queryset=Transition.objects.all(), write_only=True)
transition = serializers.PrimaryKeyRelatedField(queryset=Transition.objects.all(), write_only=True, allow_null=True)
title = serializers.CharField(allow_blank=True, required=False)
class Meta:

View File

@ -0,0 +1,26 @@
# Generated by Django 3.2.12 on 2025-11-26 01:53
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('wpm', '0124_auto_20251020_1009'),
]
operations = [
migrations.AlterField(
model_name='mlogbw',
name='number',
field=models.TextField(db_index=True, verbose_name='单个编号'),
),
migrations.AddIndex(
model_name='mlogb',
index=models.Index(condition=models.Q(('material_out__isnull', False)), fields=['material_out'], name='mlogb_mat_out_not_null'),
),
migrations.AddIndex(
model_name='mlogb',
index=models.Index(condition=models.Q(('material_in__isnull', False)), fields=['material_in'], name='mlogb_mat_in_not_null'),
),
]

View File

@ -441,6 +441,20 @@ class Mlogb(BaseModel):
need_inout = models.BooleanField('是否需要出入库', default=True)
number_from = models.TextField('来源个编号', null=True, blank=True)
class Meta:
indexes = [
models.Index(
fields=["material_out"],
name="mlogb_mat_out_not_null",
condition=Q(material_out__isnull=False),
),
models.Index(
fields=["material_in"],
name="mlogb_mat_in_not_null",
condition=Q(material_in__isnull=False),
),
]
def get_tracking(self):
if self.material_in:
@ -502,7 +516,7 @@ class MlogbDefect(BaseModel):
class Mlogbw(BaseModel):
"""TN: 单个产品生产/检验日志
"""
number = models.TextField('单个编号')
number = models.TextField('单个编号', db_index=True)
mlogb = models.ForeignKey(Mlogb, verbose_name='生产记录', on_delete=models.CASCADE, related_name="w_mlogb")
mlogbw_from = models.ForeignKey("self", verbose_name='来源个', on_delete=models.CASCADE, null=True, blank=True, related_name="w_mlogbw_from")
wpr = models.ForeignKey("wpmw.wpr", verbose_name='关联产品', on_delete=models.SET_NULL

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.12 on 2025-11-26 01:53
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('wpmw', '0008_wpr_data'),
]
operations = [
migrations.AlterField(
model_name='wpr',
name='number',
field=models.CharField(blank=True, db_index=True, max_length=50, null=True, verbose_name='编号'),
),
]

View File

@ -16,7 +16,7 @@ class Wpr(BaseModel):
TN: 动态产品
"""
number = models.CharField("编号", max_length=50, null=True, blank=True)
number = models.CharField("编号", max_length=50, null=True, blank=True, db_index=True)
number_out = models.CharField("对外编号", max_length=50, null=True, blank=True, unique=True)
version = models.PositiveIntegerField("版本号", default=9999)
state = models.PositiveSmallIntegerField('状态', default=10, choices=WmStateOption.choices)

View File

@ -1,3 +1,90 @@
## 3.0.2025120109
- feat: 新增功能
- 供应商审核通过后即可添加供应商 [caoqianming]
- 校验SupplierAudit供应商名称已存在 [caoqianming]
- material list 如需获取库存数据需指定传参 [caoqianming]
- 添加部分索引 [caoqianming]
- srm -model-Papersecret 增加 organization 申请部门字段 [TianyangZhang]
- 优化mlogbw list接口速度 [caoqianming]
- srm-patent修改字段类型 [TianyangZhang]
- resignation添加ticketrelate_name [caoqianming]
- base get_object加锁时注意is_deleted过滤采用base_manager [caoqianming]
- base wf 调用方法支持静态方法 [caoqianming]
- mlogbw关于wpr的校验修改以支持手动新增 [caoqianming]
- resignatioin提交时调用的方法 [caoqianming]
- ResignationSerializer create bug [caoqianming]
- base ticketmixin添加ticket_auto_submit_on_create [caoqianming]
- resignation添加ticketMixin [caoqianming]
- base wfmixin 修改时校验 [caoqianming]
- base ticketDetail添加create_by_name [caoqianming]
- 短信发送功能未开启 [caoqianming]
- 批次号格式错误的校验 [caoqianming]
- mioitemcreate时接收count_send [caoqianming]
- mioitemw增加筛选条件 [caoqianming]
- wpr_bxerp优化mlogbw的获取 [caoqianming]
- do_out报错更明确 [caoqianming]
- 优化ana batchwork [caoqianming]
- 改版交接支持new_wm [caoqianming]
- ofm-models borrowRecord 增加借阅数量 [TianyangZhang]
- ofm-models fix bug [TianyangZhang]
- feat : ofm -vehicle fix bug [TianyangZhang]
- ofm 修改车辆model 字段 [TianyangZhang]
- ofm-service fix bug [TianyangZhang]
- featofm-service 修改 bug [TianyangZhang]
- ofm -修改 view 字段 [TianyangZhang]
- ofm 修改 ofm 字段 并重新生成迁移文件 [TianyangZhang]
- base workflow添加分类字段 [caoqianming]
- ofm-views 修改 车辆字段 [TianyangZhang]
- ofm-vehicle 修改车辆审批申请 改完按时间段进行选择 [TianyangZhang]
- mioitem添加count_send字段 [caoqianming]
- 校验改版时选择的改版物料 [caoqianming]
- 导入物料明细支持直接从名称等匹配 [caoqianming]
- 增强新批次号校验 [caoqianming]
- 提供修改编号的接口 [caoqianming]
- supplieraudit采用新工作流挂载方式 [caoqianming]
- base 添加ticketmixin可集成到viewset下以支持工作流 [caoqianming]
- base 可跳过短信发送 [caoqianming]
- 反向操作时忽略明细校验 [caoqianming]
- 拦截new_batch的错误 [caoqianming]
- fix 修改 srm 的 views [TianyangZhang]
- base wfservice创建出工单时处理人为提交人 [caoqianming]
- featsrm fix serializer [TianyangZhang]
- base handle_ticket 完善transition校验 [caoqianming]
- DatasetRecord添加filter [caoqianming]
- handover添加selectfield [caoqianming]
- 交接记录返回material_changed_fname [caoqianming]
- 改版交接必须为合批操作 [caoqianming]
- fix srm serializer [TianyangZhang]
- srm-service 去掉 platform belong_dept [TianyangZhang]
- add new model platform '平台审批' [TianyangZhang]
- base 创建数据时检验不包含id [caoqianming]
- 添加供应商审核 [caoqianming]
- base 开始编写ticketMixin可自动挂载 [caoqianming]
- ResignationSerializer返回employee_id_number [caoqianming]
- base 添加EuModelViewSet [caoqianming]
- 离职申请支持删除 [caoqianming]
- Resignationserializer返回employee_name [caoqianming]
- base handle_ticket 默认参数 [caoqianming]
- 优化bind_resignation [caoqianming]
- base 优化wf create [caoqianming]
- ResignationViewSet添加RetrieveModelMixin [caoqianming]
- bind_resignation bug [caoqianming]
- 返回TicketSimpleSerializer [caoqianming]
- fix: 问题修复
- fix : srm 修改 paperrecord 字段 cor_author 字段类型 [TianyangZhang]
- fixsrm 修改论文审批 反存 论文台账 主要修改model 字段类型 [TianyangZhang]
- srm 修改专利台账 [TianyangZhang]
- base wfmixin gen_ticket_data保存t_id转为str [caoqianming]
- fix : 修改ofm -views -vehicle && services [TianyangZhang]
- format_json_with_placeholders 处理decimal [caoqianming]
- base wf工作流分类接口detail错误 [caoqianming]
- do_in 可直接取用wm [caoqianming]
- material get_queryset忘记return qs [caoqianming]
- material增加count__gt等查询条件 [caoqianming]
- mioitem create时校验是否混批的bug [caoqianming]
- base handle_ticket处理ticket_title [caoqianming]
- bind_resignation bug [caoqianming]
- clean_data关于material的处理 [caoqianming]
## 3.0.2025110710
- feat: 新增功能
- material count字段完全由计算可得 [caoqianming]

View File

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