This commit is contained in:
TianyangZhang 2026-01-07 16:03:19 +08:00
commit f7a78431c5
15 changed files with 146 additions and 6 deletions

View File

@ -229,7 +229,7 @@ class Mgroup(CommonBModel):
# 如果当前时间在结束时间之前,属于前一天 # 如果当前时间在结束时间之前,属于前一天
else: else:
return (w_s_time - timedelta(days=1)).date(), shift return (w_s_time - timedelta(days=1)).date(), shift
# return w_s_time.date(), None raise ParseError(f"工段{self.name}的班次规则未覆盖时间点{w_s_time.strftime('%H:%M:%S')}")
class TeamMember(BaseModel): class TeamMember(BaseModel):

0
apps/rem/__init__.py Normal file
View File

3
apps/rem/admin.py Normal file
View File

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

6
apps/rem/apps.py Normal file
View File

@ -0,0 +1,6 @@
from django.apps import AppConfig
class RemConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'apps.rem'

View File

@ -0,0 +1,41 @@
# Generated by Django 3.2.12 on 2026-01-06 01:49
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
initial = True
dependencies = [
('system', '0006_auto_20241213_1249'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Project',
fields=[
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
('name', models.TextField(verbose_name='项目名称')),
('description', models.TextField(verbose_name='项目介绍')),
('start_date', models.DateField(verbose_name='开始日期')),
('end_date', models.DateField(verbose_name='结束日期')),
('participants', models.TextField(blank=True, null=True, verbose_name='项目成员')),
('note', models.TextField(blank=True, null=True, verbose_name='备注')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='project_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('files', models.ManyToManyField(blank=True, to='system.File', verbose_name='附件')),
('leader', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='项目负责人')),
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='project_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
],
options={
'abstract': False,
},
),
]

View File

15
apps/rem/models.py Normal file
View File

@ -0,0 +1,15 @@
from django.db import models
from apps.utils.models import CommonADModel
from apps.system.models import User, File
# Create your models here.
class Project(CommonADModel):
name = models.TextField("项目名称")
description = models.TextField("项目介绍")
start_date = models.DateField("开始日期")
end_date = models.DateField("结束日期")
leader = models.ForeignKey(User, verbose_name="项目负责人", on_delete=models.CASCADE)
participants = models.TextField("项目成员", blank=True, null=True)
files = models.ManyToManyField(File, verbose_name="附件", blank=True)
note = models.TextField("备注", blank=True, null=True)

17
apps/rem/serializers.py Normal file
View File

@ -0,0 +1,17 @@
from apps.rem.models import Project
from apps.utils.serializers import CustomModelSerializer
from apps.system.serializers import FileSerializer
from rest_framework import serializers
class ProjectSerializer(CustomModelSerializer):
leader_name = serializers.CharField(source="leader.name", read_only=True)
files_ = FileSerializer(source="files", many=True, read_only=True)
class Meta:
model = Project
fields = '__all__'
class ProjectUpdateSerializer(CustomModelSerializer):
class Meta:
model = Project
fields = ["id", "participants", "files", "note"]

3
apps/rem/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

12
apps/rem/urls.py Normal file
View File

@ -0,0 +1,12 @@
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import ProjectViewSet
API_BASE_URL = 'api/rem/'
HTML_BASE_URL = 'dhtml/rem/'
router = DefaultRouter()
router.register('project', ProjectViewSet, basename='research_project')
urlpatterns = [
path(API_BASE_URL, include(router.urls)),
]

12
apps/rem/views.py Normal file
View File

@ -0,0 +1,12 @@
from django.shortcuts import render
from apps.utils.viewsets import CustomModelViewSet
from apps.rem.models import Project
from apps.rem.serializers import ProjectSerializer, ProjectUpdateSerializer
# Create your views here.
class ProjectViewSet(CustomModelViewSet):
queryset = Project.objects.all()
serializer_class = ProjectSerializer
update_serializer_class = ProjectUpdateSerializer
select_related_fields = ['leader']
search_fields = ["name", "description", "leader__name"]

View File

@ -1,6 +1,6 @@
from apps.wpm.models import BatchSt from apps.wpm.models import BatchSt
import logging import logging
from apps.qm.models import Defect from apps.qm.models import Defect, FtestWork, FtestworkDefect
from apps.wpm.models import Mlogb, MlogbDefect from apps.wpm.models import Mlogb, MlogbDefect
from apps.mtm.models import Mgroup from apps.mtm.models import Mgroup
import decimal import decimal
@ -117,25 +117,54 @@ def main(batch: str, mgroup_obj):
data[f"外观检验_返修_缺陷_{item['defect__name']}"] = item["total"] data[f"外观检验_返修_缺陷_{item['defect__name']}"] = item["total"]
data[f"外观检验_返修_缺陷_{item['defect__name']}_比例"] = round((item["total"] / data["外观检验_返修_count_real"])*100, 2) data[f"外观检验_返修_缺陷_{item['defect__name']}_比例"] = round((item["total"] / data["外观检验_返修_count_real"])*100, 2)
# 车间库存抽检
ft_qs = FtestWork.objects.filter(type2=FtestWork.TYPE2_SOME, wm__mgroup__name="外观检验", batch=batch, submit_time__isnull=False)
if ft_qs.exists():
data["外观检验_车间库存抽检_日期"] = []
data["外观检验_车间库存抽检_操作人"] = []
data["外观检验_车间库存抽检_count_notok"] = 0
for item in ft_qs:
if item.test_user:
data["外观检验_车间库存抽检_操作人"].append(item.test_user)
if item.test_date:
data["外观检验_车间库存抽检_日期"].append(item.test_date)
data["外观检验_车间库存抽检_count_notok"] += item.count_notok if item.count_notok else 0
data["外观检验_车间库存抽检_日期"] = list(set(data["外观检验_车间库存抽检_日期"]))
data["外观检验_车间库存抽检_日期"] = ";".join([item.strftime("%Y-%m-%d") for item in data["外观检验_车间库存抽检_日期"]])
data["外观检验_车间库存抽检_操作人"] = list(set(data["外观检验_车间库存抽检_操作人"]))
data["外观检验_车间库存抽检_操作人"] = ";".join([item.name for item in data["外观检验_车间库存抽检_操作人"]])
# 车间库存抽检缺陷
ftd_qs = FtestworkDefect.objects.filter(ftestwork__in=ft_qs, count__gt=0).values("defect__name").annotate(total=Sum("count"))
for item in ftd_qs:
data[f"外观检验_车间库存抽检_缺陷_{item['defect__name']}"] = item["total"]
if "外观检验_count_ok" in data: if "外观检验_count_ok" in data:
data["外观检验_总合格数"] = data["外观检验_count_ok"] + data["外观检验_返修_count_ok"] if "外观检验_返修_count_ok" in data else 0 data["外观检验_总合格数"] = data["外观检验_count_ok"] + data.get("外观检验_返修_count_ok", 0)
try: try:
data["外观检验_总合格率"] = round((data["外观检验_总合格数"] / data["外观检验_count_real"])*100, 2) data["外观检验_总合格率"] = round((data["外观检验_总合格数"] / data["外观检验_count_real"])*100, 2)
except decimal.InvalidOperation: except decimal.InvalidOperation:
data["外观检验_总合格率"] = 0 data["外观检验_总合格率"] = 0
data["外观检验_完全总合格数"] = data["外观检验_count_ok_full"] + data["外观检验_返修_count_ok_full"] if "外观检验_返修_count_ok_full" in data else 0 data["外观检验_完全总合格数"] = data["外观检验_count_ok_full"] + data.get("外观检验_返修_count_ok_full", 0)
try: try:
data["外观检验_完全总合格率"] = round((data["外观检验_完全总合格数"] / data["外观检验_count_real"])*100, 2) data["外观检验_完全总合格率"] = round((data["外观检验_完全总合格数"] / data["外观检验_count_real"])*100, 2)
except decimal.InvalidOperation: except decimal.InvalidOperation:
data["外观检验_完全总合格率"] = 0 data["外观检验_完全总合格率"] = 0
data["外观检验_直通合格数"] = data["外观检验_总合格数"] - data.get("外观检验_车间库存抽检_count_notok", 0)
if "尺寸检验_合格率" in data: if "尺寸检验_合格率" in data:
try: try:
data["外观检验_直通合格率"] = round((data["外观检验_总合格率"]* data["尺寸检验_合格率"])/100, 2) data["外观检验_直通合格率"] = round((data["外观检验_总合格率"]* data["尺寸检验_合格率"])/100, 2)
except decimal.InvalidOperation: except decimal.InvalidOperation:
data["外观检验_直通合格率"] = 0 data["外观检验_直通合格率"] = 0
try:
data["外观检验_直通合格率2"] = round((data["外观检验_直通合格数"]/data["尺寸检验_count_use"])*100, 2)
except (decimal.InvalidOperation, ZeroDivisionError):
data["外观检验_直通合格率2"] = 0
if "尺寸检验_完全合格率" in data: if "尺寸检验_完全合格率" in data:
try: try:
data["外观检验_完全直通合格率"] = round((data["外观检验_完全总合格率"]* data["尺寸检验_完全合格率"])/100, 2) data["外观检验_完全直通合格率"] = round((data["外观检验_完全总合格率"]* data["尺寸检验_完全合格率"])/100, 2)

View File

@ -30,8 +30,8 @@ class WprViewSet(CustomListModelMixin, RetrieveModelMixin, ComplexQueryMixin, Cu
ordering_fields = ["number", "create_time", "update_time"] ordering_fields = ["number", "create_time", "update_time"]
search_fields = ["number", "material__name", "material__model", "material__specification", "number_out"] search_fields = ["number", "material__name", "material__model", "material__specification", "number_out"]
annotate_dict = { annotate_dict = {
"number_prefix": RawSQL("regexp_replace(number, '(\\d+)$', '')", []), "number_prefix": RawSQL("regexp_replace(wpmw_wpr.number, '(\\d+)$', '')", []),
"number_suffix": RawSQL("COALESCE(NULLIF(regexp_replace(number, '.*?(\\d+)$', '\\1'), ''), '0')::bigint", []), "number_suffix": RawSQL("COALESCE(NULLIF(regexp_replace(wpmw_wpr.number, '.*?(\\d+)$', '\\1'), ''), '0')::bigint", []),
} }
def filter_queryset(self, queryset): def filter_queryset(self, queryset):

View File

@ -88,6 +88,7 @@ INSTALLED_APPS = [
'apps.ofm', 'apps.ofm',
'apps.srm', 'apps.srm',
'apps.asm', 'apps.asm',
'apps.rem'
] ]
MIDDLEWARE = [ MIDDLEWARE = [

View File

@ -78,6 +78,7 @@ urlpatterns = [
path('', include('apps.ofm.urls')), path('', include('apps.ofm.urls')),
path('', include('apps.srm.urls')), path('', include('apps.srm.urls')),
path('', include('apps.asm.urls')), path('', include('apps.asm.urls')),
path('', include('apps.rem.urls')),
# 前端页面入口 # 前端页面入口
path('', TemplateView.as_view(template_name="index.html")), path('', TemplateView.as_view(template_name="index.html")),