diff --git a/apps/cm/__init__.py b/apps/cm/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/cm/admin.py b/apps/cm/admin.py new file mode 100644 index 00000000..8c38f3f3 --- /dev/null +++ b/apps/cm/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/apps/cm/apps.py b/apps/cm/apps.py new file mode 100644 index 00000000..c6213e16 --- /dev/null +++ b/apps/cm/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class CmConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'apps.cm' diff --git a/apps/cm/migrations/0001_initial.py b/apps/cm/migrations/0001_initial.py new file mode 100644 index 00000000..9e932c92 --- /dev/null +++ b/apps/cm/migrations/0001_initial.py @@ -0,0 +1,36 @@ +# Generated by Django 3.2.12 on 2024-10-11 07:17 + +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('pum', '0008_auto_20240731_1829'), + ('mtm', '0042_auto_20241010_1140'), + ] + + operations = [ + migrations.CreateModel( + name='LableMat', + 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='删除标记')), + ('state', models.PositiveSmallIntegerField(choices=[(10, '合格'), (20, '不合格'), (30, '返修'), (40, '检验'), (50, '报废')], default=10, verbose_name='状态')), + ('batch', models.CharField(max_length=100, verbose_name='批次号')), + ('notok_sign', models.CharField(blank=True, max_length=10, null=True, verbose_name='不合格标记')), + ('material', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.material')), + ('material_origin', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='lm_mo', to='mtm.material', verbose_name='原始物料')), + ('supplier', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='pum.supplier', verbose_name='外协供应商')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/apps/cm/migrations/__init__.py b/apps/cm/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/cm/models.py b/apps/cm/models.py new file mode 100644 index 00000000..4f77be02 --- /dev/null +++ b/apps/cm/models.py @@ -0,0 +1,13 @@ +from django.db import models +from apps.utils.models import BaseModel +from apps.mtm.models import Material +from apps.pum.models import Supplier +# Create your models here. + +class LableMat(BaseModel): + state = models.PositiveSmallIntegerField('状态', default=10, choices=((10, '合格'), (20, '不合格'), (30, '返修'), (40, '检验'), (50, '报废'))) + material = models.ForeignKey(Material, on_delete=models.CASCADE) + batch = models.CharField('批次号', max_length=100) + supplier = models.ForeignKey(Supplier, verbose_name='外协供应商', on_delete=models.SET_NULL, null=True, blank=True) + notok_sign = models.CharField('不合格标记', max_length=10, null=True, blank=True) + material_origin = models.ForeignKey(Material, verbose_name='原始物料', on_delete=models.SET_NULL, null=True, blank=True, related_name='lm_mo') \ No newline at end of file diff --git a/apps/cm/serializers.py b/apps/cm/serializers.py new file mode 100644 index 00000000..66f59ea8 --- /dev/null +++ b/apps/cm/serializers.py @@ -0,0 +1,25 @@ +from rest_framework import serializers +from .models import LableMat +from apps.qm.models import NotOkOption +from apps.wpm.models import WmStateOption + + +class TidSerializer(serializers.Serializer): + tid = serializers.CharField(label='表ID') + + +class LabelMatSerializer(serializers.ModelSerializer): + material_name = serializers.StringRelatedField(source='material', read_only=True) + material_origin_name = serializers.StringRelatedField(source='material_origin', read_only=True) + supplier_name = serializers.CharField(source='supplier.name', read_only=True) + notok_sign_name = serializers.SerializerMethodField() + state_name = serializers.SerializerMethodField() + class Meta: + model = LableMat + fields = '__all__' + + def get_notok_sign_name(self, obj): + return getattr(NotOkOption, obj.notok_sign, NotOkOption.qt).label if obj.notok_sign else None + + def get_state_name(self, obj): + return getattr(WmStateOption, str(obj.state), WmStateOption.OK).label if obj.state else None \ No newline at end of file diff --git a/apps/cm/tests.py b/apps/cm/tests.py new file mode 100644 index 00000000..7ce503c2 --- /dev/null +++ b/apps/cm/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/apps/cm/urls.py b/apps/cm/urls.py new file mode 100644 index 00000000..652b90c8 --- /dev/null +++ b/apps/cm/urls.py @@ -0,0 +1,12 @@ +from django.urls import path, include +from rest_framework.routers import DefaultRouter +from apps.cm.views import LableMatViewSet + +API_BASE_URL = 'api/cm/' +HTML_BASE_URL = 'cm/' + +router = DefaultRouter() +router.register('labelmat', LableMatViewSet, basename='labelmat') +urlpatterns = [ + path(API_BASE_URL, include(router.urls)), +] \ No newline at end of file diff --git a/apps/cm/views.py b/apps/cm/views.py new file mode 100644 index 00000000..0c3952ca --- /dev/null +++ b/apps/cm/views.py @@ -0,0 +1,61 @@ +from apps.cm.models import LableMat +from rest_framework.decorators import action +from apps.cm.serializers import TidSerializer, LabelMatSerializer +from apps.inm.models import MaterialBatch +from apps.wpm.models import WMaterial +from rest_framework.exceptions import ParseError, NotFound +from rest_framework.response import Response +from apps.utils.viewsets import CustomGenericViewSet, RetrieveModelMixin, CustomListModelMixin +# Create your views here. + +class LableMatViewSet(CustomListModelMixin, RetrieveModelMixin, CustomGenericViewSet): + perms_map = {"post": "*", "get": "*"} + serializer_class = LabelMatSerializer + queryset = LableMat.objects.all() + select_related_fields = ["material", "material_origin", "supplier"] + + @action(methods=['post'], detail=False, serializer_class=TidSerializer) + def get_from_mb(self, request, pk=None): + """ + 从仓库明细获取物料标签 + + 从仓库明细获取物料标签 + """ + tid = request.data.get('tid') + try: + mb = MaterialBatch.objects.get(id=tid) + except MaterialBatch.DoesNotExist: + raise NotFound('仓库明细不存在') + obj, _ = LableMat.objects.get_or_create( + state=10, + material=mb.material, + batch=mb.batch, + defaults={ + "supplier": mb.supplier + } + ) + rdata = LabelMatSerializer(obj).data + rdata["code_label"] = f"mat:{obj.id}" + return Response(rdata) + + @action(methods=['post'], detail=False, serializer_class=TidSerializer) + def get_from_wm(self, request, pk=None): + """ + 从车间库存明细获取物料标签 + + 从车间库存明细获取物料标签 + """ + tid = request.data.get('tid') + try: + wm = WMaterial.objects.get(id=tid) + except WMaterial.DoesNotExist: + raise NotFound('车间库存不存在') + obj, _ = LableMat.objects.get_or_create( + state=wm.state, + material=wm.material, + batch=wm.batch, + notok_sign=wm.notok_sign, + material_origin=wm.material_origin) + rdata = LabelMatSerializer(obj).data + rdata["code_label"] = f"mat:{obj.id}" + return Response(rdata) \ No newline at end of file diff --git a/apps/inm/migrations/0020_materialbatch_supplier.py b/apps/inm/migrations/0020_materialbatch_supplier.py new file mode 100644 index 00000000..0ebaa8d9 --- /dev/null +++ b/apps/inm/migrations/0020_materialbatch_supplier.py @@ -0,0 +1,20 @@ +# Generated by Django 3.2.12 on 2024-10-11 04:38 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('pum', '0008_auto_20240731_1829'), + ('inm', '0019_mio_mgroup'), + ] + + operations = [ + migrations.AddField( + model_name='materialbatch', + name='supplier', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='pum.supplier', verbose_name='供应商'), + ), + ] diff --git a/apps/inm/models.py b/apps/inm/models.py index c14b6afc..e0ac4ba3 100644 --- a/apps/inm/models.py +++ b/apps/inm/models.py @@ -28,6 +28,8 @@ class MaterialBatch(BaseModel): count = models.DecimalField('存量', max_digits=12, decimal_places=3, default=0) production_dept = models.ForeignKey('system.dept', verbose_name='生产车间', on_delete=models.CASCADE, null=True, blank=True) expiration_date = models.DateField('有效期', null=True, blank=True) + supplier = models.ForeignKey( + Supplier, verbose_name='供应商', on_delete=models.SET_NULL, null=True, blank=True) class Meta: unique_together = ('material', 'batch', 'warehouse') diff --git a/apps/inm/serializers.py b/apps/inm/serializers.py index 866c337b..b4d6f957 100644 --- a/apps/inm/serializers.py +++ b/apps/inm/serializers.py @@ -33,6 +33,8 @@ class MaterialBatchSerializer(CustomModelSerializer): source='warehouse.name', read_only=True) material_name = serializers.StringRelatedField( source='material', read_only=True) + supplier_name = serializers.StringRelatedField( + source='supplier', read_only=True) material_ = MaterialSerializer(source='material', read_only=True) class Meta: @@ -49,6 +51,8 @@ class MaterialBatchDetailSerializer(CustomModelSerializer): material_ = MaterialSerializer(source='material', read_only=True) assemb = MaterialBatchAListSerializer( source='a_mb', read_only=True, many=True) + supplier_name = serializers.StringRelatedField( + source='supplier', read_only=True) class Meta: model = MaterialBatch diff --git a/apps/inm/services.py b/apps/inm/services.py index 78c5e672..e561f58f 100644 --- a/apps/inm/services.py +++ b/apps/inm/services.py @@ -90,7 +90,7 @@ class InmService: material=material, warehouse=warehouse, batch=i.batch, - defaults={"material": material, "warehouse": warehouse, "count": 0, "batch": i.batch} + defaults={"count": 0, "supplier": i.mio.supplier} ) if in_or_out == 1: mb.count = mb.count + getattr(i, field) diff --git a/apps/inm/views.py b/apps/inm/views.py index 5e879f1c..4c6cdbc8 100644 --- a/apps/inm/views.py +++ b/apps/inm/views.py @@ -48,7 +48,7 @@ class MaterialBatchViewSet(ListModelMixin, CustomGenericViewSet): queryset = MaterialBatch.objects.filter(count__gt=0) serializer_class = MaterialBatchSerializer retrieve_serializer_class = MaterialBatchDetailSerializer - select_related_fields = ['warehouse', 'material'] + select_related_fields = ['warehouse', 'material', 'supplier'] filterset_class = MaterialBatchFilter search_fields = ['material__name', 'material__number', 'material__model', 'material__specification', 'batch'] diff --git a/apps/wpm/models.py b/apps/wpm/models.py index 4a02cddf..f5bf9c5b 100644 --- a/apps/wpm/models.py +++ b/apps/wpm/models.py @@ -9,6 +9,7 @@ from apps.system.models import Dept from datetime import timedelta from apps.pum.models import Supplier from django.db.models import Sum +from django.utils.translation import gettext_lazy as _ # Create your models here. @@ -86,6 +87,12 @@ class SfLogExp(CommonADModel): class Meta: unique_together = ('sflog', 'stlog') +class WmStateOption(models.IntegerChoices): + OK = 10, _("合格") + NOTOK = 20, _("不合格") + REPAIR = 30, _("返修") + TEST = 40, _("检验") + SCRAP = 50, _("报废") class WMaterial(CommonBDModel): """ belong_dept是所在车间 diff --git a/server/settings.py b/server/settings.py index d6701e6a..0874cbaf 100755 --- a/server/settings.py +++ b/server/settings.py @@ -78,7 +78,8 @@ INSTALLED_APPS = [ 'apps.pm', 'apps.enp', 'apps.edu', - 'apps.dpm' + 'apps.dpm', + 'apps.cm' ] MIDDLEWARE = [ diff --git a/server/urls.py b/server/urls.py index 2ef416a7..e1e58a59 100755 --- a/server/urls.py +++ b/server/urls.py @@ -68,6 +68,7 @@ urlpatterns = [ path('', include('apps.enp.urls')), path('', include('apps.edu.urls')), path('', include('apps.dpm.urls')), + path('', include('apps.cm.urls')), # 前端页面入口 path('', TemplateView.as_view(template_name="index.html")),