feat: material count字段完全由计算可得

This commit is contained in:
caoqianming 2025-11-06 23:59:51 +08:00
parent 8bfc630919
commit 7dd15c57da
8 changed files with 57 additions and 134 deletions

View File

@ -1,45 +0,0 @@
from .models import MaterialBatch, MIOItem
from apps.mtm.models import Material, Mgroup
from apps.system.models import Dept
from rest_framework.exceptions import ParseError
from django.db.models import F, Sum
from django.db import transaction
from .services import InmService
def correct_material_batch():
"""矫正物料批次
"""
mgroups = Mgroup.objects.all()
p_dict = {}
for mgroup in mgroups:
if mgroup.process:
processId = mgroup.process.id
dept: Dept = mgroup.belong_dept
if processId not in p_dict:
p_dict[processId] = dept
else:
raise ParseError('存在多个同工序的工段:{}'.format(mgroup.name))
mbs = MaterialBatch.objects.filter(material__type__in=[Material.MA_TYPE_GOOD, Material.MA_TYPE_HALFGOOD],
production_dept=None)
for mb in mbs:
if mb.material.process:
processId = mb.material.process.id
mb.production_dept = p_dict[processId]
mb.save()
def correct_mb_count_notok():
"""矫正因count_notok未记录导致的错误数据
"""
mis = MIOItem.objects.filter(mio__state=20, count_notok=0).exclude(
test_date=None,
count_notok=F('count_n_zw') + F('count_n_tw') + F('count_n_qp') + F('count_n_wq') + F('count_n_dl') + F('count_n_pb') + F('count_n_dxt') + F('count_n_js') + F('count_n_qx') + F('count_n_zz') + F('count_n_ysq') + F('count_n_hs') + F('count_n_b') + F('count_n_qt')
)
for mi in mis:
count_notok = mi.count_n_zw + mi.count_n_tw + mi.count_n_qp + mi.count_n_wq + mi.count_n_dl + mi.count_n_pb + mi.count_n_dxt + mi.count_n_js + mi.count_n_qx + mi.count_n_zz + mi.count_n_ysq + mi.count_n_hs + mi.count_n_b + mi.count_n_qt
# 先处理库存
try:
MIOItem.objects.filter(id=mi.id).update(count_notok=count_notok)
InmService.update_mb_after_test(mi)
except ParseError as e:
MIOItem.objects.filter(id=mi.id).update(test_date=None)

View File

@ -3,7 +3,6 @@ from apps.inm.models import (MIO, MIOItem,
MIOItemA, MIOItemw) MIOItemA, MIOItemw)
from rest_framework.exceptions import ParseError from rest_framework.exceptions import ParseError
from apps.mtm.models import Material from apps.mtm.models import Material
from apps.mtm.services_2 import cal_material_count
from apps.wpm.models import WMaterial, BatchSt, BatchLog from apps.wpm.models import WMaterial, BatchSt, BatchLog
from apps.wpm.services_2 import ana_batch_thread from apps.wpm.services_2 import ana_batch_thread
from apps.wpmw.models import Wpr from apps.wpmw.models import Wpr
@ -268,16 +267,6 @@ def do_in(item: MIOItem):
class InmService: class InmService:
@classmethod
def update_material_count(cls, instance: MIO):
"""
更新物料数量
"""
# 统计物料数量
m_ids = list(MIOItem.objects.filter(mio=instance).values_list('material_id', flat=True))
m_ids2 = list(MIOItemA.objects.filter(mioitem__mio=instance).values_list('material_id', flat=True))
cal_material_count(m_ids+m_ids2)
@classmethod @classmethod
def update_inm(cls, instance: MIO, is_reverse: bool = False): def update_inm(cls, instance: MIO, is_reverse: bool = False):
""" """
@ -484,5 +473,4 @@ class InmService:
mioitem.delete() mioitem.delete()
else: else:
raise ParseError("不支持该出入库单明细撤销") raise ParseError("不支持该出入库单明细撤销")
cal_material_count([mioitem.material.id])

View File

@ -2,7 +2,6 @@ from rest_framework.exceptions import ParseError
from apps.mtm.models import Process, Material from apps.mtm.models import Process, Material
from apps.inm.models import WareHouse, MaterialBatch, MIOItem, MIOItemw, MIO from apps.inm.models import WareHouse, MaterialBatch, MIOItem, MIOItemw, MIO
from apps.utils.tools import ranstr from apps.utils.tools import ranstr
from apps.mtm.services_2 import cal_material_count
def daoru_mb(path: str): def daoru_mb(path: str):
""" """
@ -59,7 +58,6 @@ def daoru_mb(path: str):
MaterialBatch.objects.get_or_create( MaterialBatch.objects.get_or_create(
material=material, batch=batch, warehouse=warehouse, defaults={"material": material, "batch": batch, "warehouse": warehouse, "count": count, "id": idWorker.get_id()} material=material, batch=batch, warehouse=warehouse, defaults={"material": material, "batch": batch, "warehouse": warehouse, "count": count, "id": idWorker.get_id()}
) )
cal_material_count([material.id])
i = i + 1 i = i + 1
def daoru_mioitem_test(path:str, mioitem:MIOItem): def daoru_mioitem_test(path:str, mioitem:MIOItem):

View File

@ -223,7 +223,6 @@ class MIOViewSet(CustomModelViewSet):
ins.state = MIO.MIO_SUBMITED ins.state = MIO.MIO_SUBMITED
ins.save() ins.save()
InmService.update_inm(ins) InmService.update_inm(ins)
InmService.update_material_count(ins)
return Response(MIOListSerializer(instance=ins).data) return Response(MIOListSerializer(instance=ins).data)
@action(methods=['post'], detail=True, perms_map={'post': 'mio.submit'}, serializer_class=serializers.Serializer) @action(methods=['post'], detail=True, perms_map={'post': 'mio.submit'}, serializer_class=serializers.Serializer)
@ -245,7 +244,6 @@ class MIOViewSet(CustomModelViewSet):
ins.submit_time = None ins.submit_time = None
ins.save() ins.save()
InmService.update_inm(ins, is_reverse=True) InmService.update_inm(ins, is_reverse=True)
InmService.update_material_count(ins)
return Response() return Response()
@ -408,7 +406,6 @@ class MIOItemViewSet(CustomListModelMixin, BulkCreateModelMixin, BulkDestroyMode
sr.save() sr.save()
# 开始变动库存 # 开始变动库存
InmService.update_mb_item(ins, -1, 'count_notok') InmService.update_mb_item(ins, -1, 'count_notok')
InmService.update_material_count(ins.mio)
return Response() return Response()
@action(methods=['post'], detail=True, perms_map={'post': 'mioitem.test'}, serializer_class=serializers.Serializer) @action(methods=['post'], detail=True, perms_map={'post': 'mioitem.test'}, serializer_class=serializers.Serializer)
@ -426,7 +423,6 @@ class MIOItemViewSet(CustomListModelMixin, BulkCreateModelMixin, BulkDestroyMode
pass pass
ins.test_date = None ins.test_date = None
ins.save() ins.save()
InmService.update_material_count(ins.mio)
return Response() return Response()
@action(methods=['post'], detail=True, perms_map={'post': 'mioitem.test'}, serializer_class=MIOItemPurInTestSerializer) @action(methods=['post'], detail=True, perms_map={'post': 'mioitem.test'}, serializer_class=MIOItemPurInTestSerializer)
@ -444,7 +440,6 @@ class MIOItemViewSet(CustomListModelMixin, BulkCreateModelMixin, BulkDestroyMode
sr = MIOItemPurInTestSerializer(instance=ins, data=request.data) sr = MIOItemPurInTestSerializer(instance=ins, data=request.data)
sr.is_valid(raise_exception=True) sr.is_valid(raise_exception=True)
sr.save() sr.save()
InmService.update_material_count(ins.mio)
return Response() return Response()
@action(methods=['post'], detail=False, perms_map={'post': '*'}, serializer_class=MioItemAnaSerializer) @action(methods=['post'], detail=False, perms_map={'post': '*'}, serializer_class=MioItemAnaSerializer)

View File

@ -33,6 +33,18 @@ class MaterialSimpleSerializer(CustomModelSerializer):
class MaterialSerializer(CustomModelSerializer): class MaterialSerializer(CustomModelSerializer):
process_name = serializers.CharField(source='process.name', read_only=True) process_name = serializers.CharField(source='process.name', read_only=True)
full_name = serializers.SerializerMethodField() full_name = serializers.SerializerMethodField()
count_mb = serializers.SerializerMethodField()
count_wm = serializers.SerializerMethodField()
count = serializers.SerializerMethodField()
def get_count_mb(self, obj):
return getattr(obj, 'count_mb', 0)
def get_count_wm(self, obj):
return getattr(obj, 'count_wm', 0)
def get_count(self, obj):
return getattr(obj, 'count_mb', 0) + getattr(obj, 'count_wm', 0)
class Meta: class Meta:
model = Material model = Material

View File

@ -4,24 +4,24 @@ from django.db.models import Sum
from apps.inm.models import MaterialBatch from apps.inm.models import MaterialBatch
from apps.wpm.models import WMaterial from apps.wpm.models import WMaterial
def cal_material_count(materialId_list: List[str]=None): # def cal_material_count(materialId_list: List[str]=None):
""" # """
计算物料总数量 # 计算物料总数量
""" # """
if materialId_list is None: # if materialId_list is None:
materialId_list = [] # materialId_list = []
if materialId_list: # if materialId_list:
objs = Material.objects.filter(id__in=set(materialId_list)) # objs = Material.objects.filter(id__in=set(materialId_list))
else: # else:
objs = Material.objects.all() # objs = Material.objects.all()
for material in objs: # for material in objs:
mb_count = MaterialBatch.objects.filter(material=material, state=10).aggregate(total=Sum("count"))["total"] or 0 # mb_count = MaterialBatch.objects.filter(material=material, state=10).aggregate(total=Sum("count"))["total"] or 0
wm_count = WMaterial.objects.filter(material=material, state=10).aggregate(total=Sum("count"))["total"] or 0 # wm_count = WMaterial.objects.filter(material=material, state=10).aggregate(total=Sum("count"))["total"] or 0
if mb_count is None: # if mb_count is None:
mb_count = 0 # mb_count = 0
if wm_count is None: # if wm_count is None:
wm_count = 0 # wm_count = 0
Material.objects.filter(id=material.id).update( # Material.objects.filter(id=material.id).update(
count_wm=wm_count, # count_wm=wm_count,
count_mb=mb_count, # count_mb=mb_count,
count=mb_count + wm_count) # count=mb_count + wm_count)

View File

@ -17,12 +17,13 @@ from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet
from apps.utils.mixins import BulkCreateModelMixin, BulkDestroyModelMixin, CustomListModelMixin from apps.utils.mixins import BulkCreateModelMixin, BulkDestroyModelMixin, CustomListModelMixin
from rest_framework.serializers import Serializer from rest_framework.serializers import Serializer
from django.db import transaction from django.db import transaction
from django.db.models import Q
from apps.wf.models import Ticket from apps.wf.models import Ticket
from django.utils import timezone from django.utils import timezone
from rest_framework.permissions import IsAdminUser from rest_framework.permissions import IsAdminUser
from apps.utils.export import export_excel from apps.utils.export import export_excel
from operator import itemgetter from operator import itemgetter
from django.db.models import Sum, Q, Value, F, ExpressionWrapper, DecimalField
from django.db.models.functions import Coalesce
# Create your views here. # Create your views here.
class MaterialViewSet(CustomModelViewSet): class MaterialViewSet(CustomModelViewSet):
@ -40,6 +41,28 @@ class MaterialViewSet(CustomModelViewSet):
'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', 'create_time']
def get_queryset(self):
qs = super().get_queryset()
if self.action in ["list", "retrieve"]:
return qs.annotate(
count_wm=Coalesce(
Sum('mb_m__count', filter=Q(mb_m__state=10)),
Value(0),
output_field=DecimalField()
),
count_mb=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()
)
)
def perform_destroy(self, instance): def perform_destroy(self, instance):
from apps.inm.models import MaterialBatch from apps.inm.models import MaterialBatch
@ -60,18 +83,6 @@ class MaterialViewSet(CustomModelViewSet):
daoru_material(settings.BASE_DIR + request.data.get('path', '')) daoru_material(settings.BASE_DIR + request.data.get('path', ''))
return Response() return Response()
@action(methods=['post'], detail=True, serializer_class=Serializer, perms_map={'post': 'material.create'})
@transaction.atomic
def cal_count(self, request, *args, **kwargs):
"""统计数量
统计数量
"""
ins = self.get_object()
from apps.mtm.services_2 import cal_material_count
cal_material_count([ins.id])
return Response()
@action(methods=['put'], detail=True, serializer_class=Serializer, perms_map={'put': '*'}) @action(methods=['put'], detail=True, serializer_class=Serializer, perms_map={'put': '*'})
@transaction.atomic @transaction.atomic
def set_week_esitimate_consume(self, request, *args, **kwargs): def set_week_esitimate_consume(self, request, *args, **kwargs):

View File

@ -12,7 +12,6 @@ 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, MlogUser
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
import logging import logging
@ -23,7 +22,6 @@ from ..qm.models import Defect, Ftest
from django.db.models import Count, Q from django.db.models import Count, Q
from apps.utils.tasks import ctask_run from apps.utils.tasks import ctask_run
from apps.mtm.models import Process from apps.mtm.models import Process
from apps.mtm.services_2 import cal_material_count
from apps.utils.lock import lock_model_record_d_func from apps.utils.lock import lock_model_record_d_func
myLogger = logging.getLogger('log') myLogger = logging.getLogger('log')
@ -399,9 +397,6 @@ def mlog_submit(mlog: Mlog, user: User, now: Union[datetime.datetime, None]):
# 更新任务进度 # 更新任务进度
cal_mtask_progress_from_mlog(mlog) cal_mtask_progress_from_mlog(mlog)
# 更新物料数量
cal_material_count_from_mlog(mlog)
# 触发批次统计分析 # 触发批次统计分析
xbatches = list(Mlogb.objects.filter(mlog=mlog).values_list('batch', flat=True)) xbatches = list(Mlogb.objects.filter(mlog=mlog).values_list('batch', flat=True))
ana_batch_thread(xbatchs=xbatches, mgroup=mlog.mgroup) ana_batch_thread(xbatchs=xbatches, mgroup=mlog.mgroup)
@ -618,9 +613,6 @@ def mlog_revert(mlog: Mlog, user: User, now: Union[datetime.datetime, None]):
# 更新任务进度 # 更新任务进度
cal_mtask_progress_from_mlog(mlog) cal_mtask_progress_from_mlog(mlog)
# 更新物料数量
cal_material_count_from_mlog(mlog)
# 清除关系链 # 清除关系链
BatchLog.clear(mlog=mlog) BatchLog.clear(mlog=mlog)
@ -650,24 +642,6 @@ def cal_mtask_progress_from_mlog(mlog:Mlog):
continue continue
update_mtask(mtask, fill_way=mlog.fill_way) update_mtask(mtask, fill_way=mlog.fill_way)
caled_mtask.append(mtask) caled_mtask.append(mtask)
def cal_material_count_from_mlog(mlog: Mlog):
"""
更新mlog关联的物料数量(可单独执行)
"""
matid_list = []
if mlog.material_in:
matid_list.append(mlog.material_in.id)
if mlog.material_out:
matid_list.append(mlog.material_out.id)
matid_list2 = Mlogb.objects.filter(mlog=mlog).values_list('material_in__id', 'material_out__id').distinct()
for matid in matid_list2:
if matid[0]:
matid_list.append(matid[0])
if matid[1]:
matid_list.append(matid[1])
matid_list = list(set(matid_list))
cal_material_count(matid_list)
def update_mtask(mtask: Mtask, fill_way: int = 10): def update_mtask(mtask: Mtask, fill_way: int = 10):
@ -953,11 +927,6 @@ def handover_submit(handover:Handover, user: User, now: Union[datetime.datetime,
if handover.recive_user is None: if handover.recive_user is None:
handover.recive_user = user handover.recive_user = user
handover.save() handover.save()
# 如果是改版交接需要触发统计数量
if handover.type == Handover.H_CHANGE:
mids.append(handover.material_changed.id)
cal_material_count(mids)
ana_batch_thread(xbatchs=batches) ana_batch_thread(xbatchs=batches)
@ -1013,11 +982,6 @@ def handover_revert(handover:Handover, handler:User=None):
handover.save() handover.save()
# 删除追踪链 # 删除追踪链
BatchLog.clear(handover=handover) BatchLog.clear(handover=handover)
# 如果是改版交接需要触发统计数量
if handover.type == Handover.H_CHANGE:
mids.append(handover.material_changed.id)
cal_material_count(mids)
ana_batch_thread(xbatchs=xbatchs) ana_batch_thread(xbatchs=xbatchs)