feat(wpm): enrich handover detail records

This commit is contained in:
caoqianming 2026-08-07 16:04:17 +08:00
parent 94215df0b6
commit fa16694f9f
4 changed files with 92 additions and 5 deletions

View File

@ -877,7 +877,7 @@ class Handoverb(BaseModel):
@property @property
def handoverbw(self): def handoverbw(self):
return Handoverbw.objects.filter(handoverb=self) return self.w_handoverb.all()
class Handoverbw(BaseModel): class Handoverbw(BaseModel):
"""TN: 单个产品交接记录 """TN: 单个产品交接记录

View File

@ -31,6 +31,16 @@ from apps.em.models import Equipment
from django.db.models import Q from django.db.models import Q
mylogger = logging.getLogger("log") mylogger = logging.getLogger("log")
WM_STATE_NAMES = {
WMaterial.WM_OK: "合格",
WMaterial.WM_NOTOK: "不合格",
WMaterial.WM_REPAIR: "返修",
WMaterial.WM_REPAIRED: "返修完成",
WMaterial.WM_TEST: "检验",
WMaterial.WM_SCRAP: "报废",
}
class OtherLogSerializer(CustomModelSerializer): class OtherLogSerializer(CustomModelSerializer):
class Meta: class Meta:
model = OtherLog model = OtherLog
@ -1286,8 +1296,35 @@ class HandoverbSerializer(CustomModelSerializer):
) )
) )
notok_sign = serializers.CharField(source='wm.notok_sign', read_only=True) notok_sign = serializers.CharField(source='wm.notok_sign', read_only=True)
notok_sign_name = serializers.SerializerMethodField()
defect_name = serializers.CharField(source="wm.defect.name", read_only=True) defect_name = serializers.CharField(source="wm.defect.name", read_only=True)
defect_grade = serializers.SerializerMethodField()
defect_grade_name = serializers.SerializerMethodField()
material_name = serializers.StringRelatedField(source="wm.material", read_only=True)
state_name = serializers.SerializerMethodField()
mgroup_name = serializers.CharField(source="wm.mgroup.name", read_only=True)
belong_dept_name = serializers.CharField(source="wm.belong_dept.name", read_only=True)
count_available = serializers.SerializerMethodField()
handoverbw = Handoverbwserializer(many=True, required=False) handoverbw = Handoverbwserializer(many=True, required=False)
def get_notok_sign_name(self, obj):
return getattr(NotOkOption, obj.wm.notok_sign, NotOkOption.qt).label if obj.wm.notok_sign else None
def get_defect_grade(self, obj):
return effective_defect_grade(obj.wm, "notok_sign")
def get_defect_grade_name(self, obj):
return DEFECT_GRADE_NAMES[self.get_defect_grade(obj)]
def get_state_name(self, obj):
return WM_STATE_NAMES.get(obj.wm.state, str(obj.wm.state))
def get_count_available(self, obj):
# 编辑未提交交接时,当前明细占用的数量仍应允许重新填写。
if obj.handover.submit_time is not None:
return obj.count
return obj.wm.count - obj.wm.count_handovering + obj.count
class Meta: class Meta:
model = Handoverb model = Handoverb
fields = "__all__" fields = "__all__"
@ -1310,9 +1347,14 @@ class HandoverSerializer(CustomModelSerializer):
recive_user_name = serializers.CharField( recive_user_name = serializers.CharField(
source='recive_user.name', read_only=True) source='recive_user.name', read_only=True)
recive_dept_name = serializers.CharField( recive_dept_name = serializers.CharField(
source='recive_dept', read_only=True) source='recive_dept.name', read_only=True)
send_dept_name = serializers.CharField(source='send_dept.name', read_only=True)
send_mgroup_name = serializers.CharField(source='send_mgroup.name', read_only=True) send_mgroup_name = serializers.CharField(source='send_mgroup.name', read_only=True)
recive_mgroup_name = serializers.CharField(source='recive_mgroup.name', read_only=True) recive_mgroup_name = serializers.CharField(source='recive_mgroup.name', read_only=True)
submit_user_name = serializers.CharField(source='submit_user.name', read_only=True)
type_name = serializers.CharField(source='get_type_display', read_only=True)
mtype_name = serializers.CharField(source='get_mtype_display', read_only=True)
state_changed_name = serializers.SerializerMethodField()
material_ = MaterialSimpleSerializer(source='material', read_only=True) material_ = MaterialSimpleSerializer(source='material', read_only=True)
material_name = serializers.StringRelatedField( material_name = serializers.StringRelatedField(
source='material', read_only=True) source='material', read_only=True)
@ -1321,6 +1363,9 @@ class HandoverSerializer(CustomModelSerializer):
handoverb = HandoverbSerializer(many=True, required=False) handoverb = HandoverbSerializer(many=True, required=False)
ticket_ = TicketSimpleSerializer(source='ticket', read_only=True) ticket_ = TicketSimpleSerializer(source='ticket', read_only=True)
def get_state_changed_name(self, obj):
return WM_STATE_NAMES.get(obj.state_changed) if obj.state_changed is not None else None
def to_internal_value(self, data): def to_internal_value(self, data):
handoverb = data.get("handoverb", []) if hasattr(data, "get") else [] handoverb = data.get("handoverb", []) if hasattr(data, "get") else []
wm_ids = { wm_ids = {

View File

@ -5,7 +5,7 @@ from django.test.utils import CaptureQueriesContext
from apps.mtm.models import Material from apps.mtm.models import Material
from apps.qm.models import Defect from apps.qm.models import Defect
from apps.system.models import Dept, User from apps.system.models import Dept, User
from apps.wpm.models import Handover, WMaterial from apps.wpm.models import Handover, Handoverb, WMaterial
from apps.wpm.serializers import HandoverSerializer from apps.wpm.serializers import HandoverSerializer
@ -56,3 +56,32 @@ class HandoverSerializerQueryTests(TestCase):
if 'FROM "wpm_wmaterial"' in query["sql"] if 'FROM "wpm_wmaterial"' in query["sql"]
] ]
self.assertEqual(len(inventory_queries), 1, inventory_queries) self.assertEqual(len(inventory_queries), 1, inventory_queries)
def test_detail_contains_display_fields_for_handover_and_items(self):
handover = Handover.objects.create(
send_date="2026-08-07",
send_user=self.user,
send_dept=self.dept,
recive_dept=self.dept,
material=self.material,
wm=self.inventories[0],
count=1,
type=Handover.H_NORMAL,
mtype=Handover.H_NORMAL,
)
Handoverb.objects.create(
handover=handover,
wm=self.inventories[0],
batch=self.inventories[0].batch,
count=1,
)
data = HandoverSerializer(handover).data
self.assertEqual(data["send_dept_name"], self.dept.name)
self.assertEqual(data["recive_dept_name"], self.dept.name)
self.assertEqual(data["type_name"], "正常交接")
self.assertEqual(data["mtype_name"], "正常")
self.assertEqual(data["handoverb"][0]["material_name"], str(self.material))
self.assertEqual(data["handoverb"][0]["state_name"], "合格")
self.assertEqual(data["handoverb"][0]["count_available"], 1)

View File

@ -592,7 +592,20 @@ class HandoverViewSet(CustomModelViewSet):
select_related_fields = ["send_user", "send_mgroup", "send_dept", "recive_user", "recive_mgroup", "recive_dept", "wm", "material_changed", "material", "material__process"] select_related_fields = ["send_user", "send_mgroup", "send_dept", "recive_user", "recive_mgroup", "recive_dept", "wm", "material_changed", "material", "material__process"]
filterset_class = HandoverFilter filterset_class = HandoverFilter
search_fields = ["material__name", "material__number", "material__specification", "batch", "material__model", "b_handover__batch", "new_batch", "wm__batch"] search_fields = ["material__name", "material__number", "material__specification", "batch", "material__model", "b_handover__batch", "new_batch", "wm__batch"]
prefetch_related_fields = [Prefetch("b_handover", queryset=Handoverb.objects.select_related("wm__defect")), "ticket__state"] prefetch_related_fields = ["ticket__state"]
def get_queryset_custom(self, queryset):
if self.action not in ["list", "retrieve"]:
return queryset
detail_queryset = Handoverb.objects.select_related(
"handover", "wm__defect", "wm__material", "wm__mgroup", "wm__belong_dept"
)
if self.action == "retrieve":
detail_queryset = detail_queryset.prefetch_related("w_handoverb")
return queryset.prefetch_related(
Prefetch("b_handover", queryset=detail_queryset)
)
def perform_destroy(self, instance: Handover): def perform_destroy(self, instance: Handover):
user = self.request.user user = self.request.user
@ -680,7 +693,7 @@ class HandoverViewSet(CustomModelViewSet):
m_qs = m_qs.filter(process__route_p__material_in__id=materialInId) | m_qs.filter(process__route_p__routemat_route__material__id=materialInId) m_qs = m_qs.filter(process__route_p__material_in__id=materialInId) | m_qs.filter(process__route_p__routemat_route__material__id=materialInId)
elif type in [Handover.H_SCRAP]: elif type in [Handover.H_SCRAP]:
m_qs = m_qs.filter(process=None) m_qs = m_qs.filter(process=None)
return Response(list(m_qs.values("id", "name").distinct())) return Response(list(m_qs.values("id", "name", "belong_dept").distinct()))
@action(methods=["post"], detail=False, perms_map={"post": "handover.create"}, serializer_class=GenHandoverWmSerializer) @action(methods=["post"], detail=False, perms_map={"post": "handover.create"}, serializer_class=GenHandoverWmSerializer)
@transaction.atomic @transaction.atomic