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 apps.mtm.models import Goal, Material, Route, RoutePack
from django.db.models.expressions import F from django.db.models.expressions import F
from rest_framework.exceptions import ParseError 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): class MaterialFilter(filters.FilterSet):
tag = filters.CharFilter(method='filter_tag', label="low_inm:库存不足") 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: class Meta:
model = Material model = Material
@ -30,7 +28,7 @@ class MaterialFilter(filters.FilterSet):
def filter_tag(self, queryset, name, value): def filter_tag(self, queryset, name, value):
if value == 'low_inm': 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')) count__lte=F('count_safe'))
return queryset return queryset

View File

@ -6,6 +6,8 @@ from collections import defaultdict, deque
from django.db.models import Q from django.db.models import Q
from datetime import datetime, timedelta from datetime import datetime, timedelta
from django.utils import timezone 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): class Process(CommonBModel):
""" """
@ -123,6 +125,25 @@ class Material(CommonAModel):
def fname(self): 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 ""}' 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): class Shift(CommonBModel):
"""TN:班次 """TN:班次

View File

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

View File

@ -44,24 +44,8 @@ class MaterialViewSet(CustomModelViewSet):
def get_queryset(self): def get_queryset(self):
qs = super().get_queryset() qs = super().get_queryset()
if self.action in ["list", "retrieve"]: if self.action in ["list", "retrieve"] and self.request.query_params.get('wiith_count', None) == 'yes':
return qs.annotate( return Material.annotate_count(qs)
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()
)
)
return qs return qs
def perform_destroy(self, instance): def perform_destroy(self, instance):

View File

@ -1,7 +1,7 @@
from rest_framework import serializers from rest_framework import serializers
from apps.utils.serializers import CustomModelSerializer from apps.utils.serializers import CustomModelSerializer
from apps.utils.constants import EXCLUDE_FIELDS_DEPT, EXCLUDE_FIELDS_BASE, EXCLUDE_FIELDS 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.pum.models import Supplier, PuPlan, PuPlanItem, PuOrder, PuOrderItem, SupplierAudit
from apps.mtm.serializers import MaterialSerializer, MaterialSimpleSerializer from apps.mtm.serializers import MaterialSerializer, MaterialSimpleSerializer
@ -152,3 +152,9 @@ class SupplierAuditSerializer(CustomModelSerializer):
model = SupplierAudit model = SupplierAudit
fields = "__all__" fields = "__all__"
read_only_fields = EXCLUDE_FIELDS_BASE + ['ticket'] 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 django.utils import timezone
from apps.pum.services import PumService from apps.pum.services import PumService
from apps.wf.mixins import TicketMixin from apps.wf.mixins import TicketMixin
from apps.wf.models import Ticket
# Create your views here. # Create your views here.
@ -42,6 +43,13 @@ class SupplierAuditViewSet(TicketMixin, CustomModelViewSet):
search_fields = ['name', 'material_name', 'material_cate'] search_fields = ['name', 'material_name', 'material_cate']
workflow_key = "wf_supplieraudit" 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): class PuPlanViewSet(CustomModelViewSet):
""" """
list: 采购计划 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): 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) title = serializers.CharField(allow_blank=True, required=False)
class Meta: 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) need_inout = models.BooleanField('是否需要出入库', default=True)
number_from = models.TextField('来源个编号', null=True, blank=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): def get_tracking(self):
if self.material_in: if self.material_in:
@ -502,7 +516,7 @@ class MlogbDefect(BaseModel):
class Mlogbw(BaseModel): class Mlogbw(BaseModel):
"""TN: 单个产品生产/检验日志 """TN: 单个产品生产/检验日志
""" """
number = models.TextField('单个编号') number = models.TextField('单个编号', db_index=True)
mlogb = models.ForeignKey(Mlogb, verbose_name='生产记录', on_delete=models.CASCADE, related_name="w_mlogb") 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") 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 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: 动态产品 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) number_out = models.CharField("对外编号", max_length=50, null=True, blank=True, unique=True)
version = models.PositiveIntegerField("版本号", default=9999) version = models.PositiveIntegerField("版本号", default=9999)
state = models.PositiveSmallIntegerField('状态', default=10, choices=WmStateOption.choices) 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 ## 3.0.2025110710
- feat: 新增功能 - feat: 新增功能
- material count字段完全由计算可得 [caoqianming] - material count字段完全由计算可得 [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 = '3.0.2025110710' SYS_VERSION = '3.0.2025120109'
X_FRAME_OPTIONS = 'SAMEORIGIN' X_FRAME_OPTIONS = 'SAMEORIGIN'
# Application definition # Application definition