feat(wpm): add files to individual outputs

This commit is contained in:
caoqianming 2026-08-03 10:22:01 +08:00
parent e23db9dc6c
commit d86d70641d
6 changed files with 98 additions and 2 deletions

View File

@ -0,0 +1,24 @@
# Generated by Django 4.2.27 on 2026-08-03
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("system", "0007_alter_dept_create_by_alter_dept_third_info_and_more"),
("wpm", "0137_mlog_wpr_defect_snapshot"),
]
operations = [
migrations.AddField(
model_name="mlogbw",
name="files",
field=models.ManyToManyField(
blank=True,
related_name="mlogbw_outputs",
to="system.file",
verbose_name="产出附件",
),
),
]

View File

@ -740,6 +740,12 @@ class Mlogbw(BaseModel):
"""TN: 单个产品生产/检验日志 """TN: 单个产品生产/检验日志
""" """
number = models.TextField('单个编号', db_index=True) number = models.TextField('单个编号', db_index=True)
files = models.ManyToManyField(
"system.File",
verbose_name="产出附件",
blank=True,
related_name="mlogbw_outputs",
)
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

@ -8,7 +8,7 @@ from .models import (SfLog, StLog, SfLogExp, WMaterial, Mlog,
Handover, Handoverb, Mlogb, AttLog, Handover, Handoverb, Mlogb, AttLog,
OtherLog, Fmlog, BatchSt, Mlogbw, Handoverbw, MlogbDefect, MlogUser, BatchLog) OtherLog, Fmlog, BatchSt, Mlogbw, Handoverbw, MlogbDefect, MlogUser, BatchLog)
from apps.system.models import Dept, User from apps.system.models import Dept, User
from apps.system.serializers import UserSimpleSerializer from apps.system.serializers import FileSerializer, UserSimpleSerializer
from apps.pm.models import Mtask, Mtaskb from apps.pm.models import Mtask, Mtaskb
from apps.wpm.tasks import cal_enstat_when_pcoal_heat_change, cal_enstat_when_team_change, cal_exp_duration_sec from apps.wpm.tasks import cal_enstat_when_pcoal_heat_change, cal_enstat_when_team_change, cal_exp_duration_sec
from apps.wpm.services import get_sflog, find_material_can_change, generate_new_batch from apps.wpm.services import get_sflog, find_material_can_change, generate_new_batch
@ -923,6 +923,8 @@ class MlogbInUpdateSerializer(CustomModelSerializer):
return ins return ins
class MlogbwCreateUpdateSerializer(CustomModelSerializer): class MlogbwCreateUpdateSerializer(CustomModelSerializer):
files = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
files_ = FileSerializer(source="files", many=True, read_only=True)
ftest = FtestProcessSerializer(required=False, allow_null=True) ftest = FtestProcessSerializer(required=False, allow_null=True)
equip_name = serializers.CharField(source='equip.name', read_only=True) equip_name = serializers.CharField(source='equip.name', read_only=True)
equip_number = serializers.CharField(source='equip.number', read_only=True) equip_number = serializers.CharField(source='equip.number', read_only=True)
@ -936,7 +938,8 @@ class MlogbwCreateUpdateSerializer(CustomModelSerializer):
fields = ["id", "number", "wpr", "note", fields = ["id", "number", "wpr", "note",
"mlogb", "ftest", "equip", "tooling", "work_start_time", "mlogb", "ftest", "equip", "tooling", "work_start_time",
"work_end_time", "mlogbw_from", "equip_name", "equip_number", "work_end_time", "mlogbw_from", "equip_name", "equip_number",
"tooling_name", "tooling_number", "wpr_number_out", "wpr_pre_info", "mlogb__batch"] "tooling_name", "tooling_number", "wpr_number_out", "wpr_pre_info", "mlogb__batch",
"files", "files_"]
read_only_fields = ["mlogbw_from"] read_only_fields = ["mlogbw_from"]
def validate(self, attrs): def validate(self, attrs):
@ -998,6 +1001,15 @@ class MlogbwCreateUpdateSerializer(CustomModelSerializer):
class MlogbwListSerializer(MlogbwCreateUpdateSerializer): class MlogbwListSerializer(MlogbwCreateUpdateSerializer):
ftest = FtestProcessListSerializer(read_only=True) ftest = FtestProcessListSerializer(read_only=True)
class MlogbwFilesSerializer(CustomModelSerializer):
files_ = FileSerializer(source="files", many=True, read_only=True)
class Meta:
model = Mlogbw
fields = ["id", "files", "files_"]
read_only_fields = ["id", "files_"]
class MlogbwStartTestSerializer(serializers.Serializer): class MlogbwStartTestSerializer(serializers.Serializer):
mlogbw_ids = serializers.ListField(child=serializers.CharField(), label="mlogbwId列表") mlogbw_ids = serializers.ListField(child=serializers.CharField(), label="mlogbwId列表")
test_equip = serializers.CharField(label="测试设备", allow_null=True, required=False, allow_blank=True) test_equip = serializers.CharField(label="测试设备", allow_null=True, required=False, allow_blank=True)

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,36 @@
from django.test import SimpleTestCase
from django.urls import reverse
from apps.system.models import File
from apps.wpm.models import Mlogbw
from apps.wpm.serializers import MlogbwFilesSerializer, MlogbwListSerializer
from apps.wpm.views import MlogbwViewSet
class MlogbwFilesDefinitionTests(SimpleTestCase):
def test_files_field_links_system_file(self):
field = Mlogbw._meta.get_field("files")
self.assertTrue(field.blank)
self.assertIs(field.remote_field.model, File)
self.assertEqual(field.remote_field.related_name, "mlogbw_outputs")
def test_list_serializer_exposes_file_ids_and_details(self):
fields = MlogbwListSerializer().fields
self.assertTrue(fields["files"].read_only)
self.assertTrue(fields["files_"].read_only)
def test_files_serializer_only_writes_file_relations(self):
fields = MlogbwFilesSerializer().fields
self.assertEqual(set(fields), {"id", "files", "files_"})
self.assertFalse(fields["files"].read_only)
def test_files_action_supports_read_and_update(self):
self.assertEqual(MlogbwViewSet.files.mapping["get"], "files")
self.assertEqual(MlogbwViewSet.files.mapping["put"], "files")
self.assertEqual(
reverse("mlogbw-files", kwargs={"pk": "single-id"}),
"/api/wpm/mlogbw/single-id/files/",
)

View File

@ -46,6 +46,7 @@ from .serializers import (
BatchStSerializer, BatchStSerializer,
MlogbwCreateUpdateSerializer, MlogbwCreateUpdateSerializer,
MlogbwListSerializer, MlogbwListSerializer,
MlogbwFilesSerializer,
HandoverMgroupSerializer, HandoverMgroupSerializer,
MlogListSerializer, MlogListSerializer,
MlogbSerializer, MlogbSerializer,
@ -1147,6 +1148,7 @@ class MlogbwViewSet(CustomModelViewSet):
filterset_class = MlogbwFilter filterset_class = MlogbwFilter
select_related_fields = ["ftest", "equip", "tooling", "wpr", "mlogb"] select_related_fields = ["ftest", "equip", "tooling", "wpr", "mlogb"]
prefetch_related_fields = [ prefetch_related_fields = [
"files",
Prefetch( Prefetch(
"ftest__items_ftest", "ftest__items_ftest",
queryset=FtestItem.objects.select_related( queryset=FtestItem.objects.select_related(
@ -1166,6 +1168,21 @@ class MlogbwViewSet(CustomModelViewSet):
ordering = ["mlogb", "number", "create_time"] ordering = ["mlogb", "number", "create_time"]
ordering_fields = ["number", "create_time", "mlogb"] ordering_fields = ["number", "create_time", "mlogb"]
@action(detail=True, methods=["get", "put"], url_path="files")
@transaction.atomic
def files(self, request, *args, **kwargs):
instance = self.get_object()
if instance.mlogb.material_out_id is None:
raise ParseError("只能给输出单件上传附件")
if request.method == "PUT":
MlogViewSet.lock_and_check_can_update(instance.mlogb.mlog)
serializer = MlogbwFilesSerializer(instance, data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
else:
serializer = MlogbwFilesSerializer(instance)
return Response(serializer.data)
def perform_create(self, serializer): def perform_create(self, serializer):
ins: Mlogbw = serializer.save() ins: Mlogbw = serializer.save()
mlog: Mlog = None mlog: Mlog = None