diff --git a/hb_server/apps/inm/filters.py b/hb_server/apps/inm/filters.py new file mode 100644 index 0000000..c71c4fb --- /dev/null +++ b/hb_server/apps/inm/filters.py @@ -0,0 +1,9 @@ +from django_filters import rest_framework as filters + +from apps.mtm.models import Material +from .models import MaterialBatch +class MbFilterSet(filters.FilterSet): + material = filters.ModelMultipleChoiceFilter(field_name="material", queryset=Material.objects.all()) + class Meta: + model = MaterialBatch + fields = ['material', 'warehouse'] \ No newline at end of file diff --git a/hb_server/apps/inm/migrations/0005_auto_20211025_1533.py b/hb_server/apps/inm/migrations/0005_auto_20211025_1533.py index be966a8..4a25c15 100644 --- a/hb_server/apps/inm/migrations/0005_auto_20211025_1533.py +++ b/hb_server/apps/inm/migrations/0005_auto_20211025_1533.py @@ -10,7 +10,6 @@ class Migration(migrations.Migration): dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('wpm', '__first__'), ('mtm', '0025_outputmaterial_is_main'), ('pm', '0007_auto_20211025_1533'), ('inm', '0004_auto_20210929_0842'), @@ -57,7 +56,6 @@ class Migration(migrations.Migration): ('batch', models.CharField(blank=True, max_length=100, null=True, verbose_name='所属批次号')), ('fifos', models.JSONField(blank=True, default=list, verbose_name='关联出入库记录')), ('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='iproduct_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')), - ('d_product', models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.CASCADE, to='wpm.product', verbose_name='关联的动态产品')), ('material', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.material', verbose_name='物料类型')), ('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='iproduct_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')), ('warehouse', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='inm.warehouse', verbose_name='所在仓库')), diff --git a/hb_server/apps/inm/migrations/0006_auto_20211027_0941.py b/hb_server/apps/inm/migrations/0006_auto_20211027_0941.py index d637e16..8598dc5 100644 --- a/hb_server/apps/inm/migrations/0006_auto_20211027_0941.py +++ b/hb_server/apps/inm/migrations/0006_auto_20211027_0941.py @@ -7,7 +7,6 @@ import django.db.models.deletion class Migration(migrations.Migration): dependencies = [ - ('wpm', '__first__'), ('inm', '0005_auto_20211025_1533'), ] @@ -18,9 +17,4 @@ class Migration(migrations.Migration): field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='inm.fifo', verbose_name='关联出入库'), preserve_default=False, ), - migrations.AlterField( - model_name='iproduct', - name='d_product', - field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.CASCADE, to='wpm.product', verbose_name='关联的动态产品'), - ), ] diff --git a/hb_server/apps/inm/models.py b/hb_server/apps/inm/models.py index 03d6b72..9a2ac61 100644 --- a/hb_server/apps/inm/models.py +++ b/hb_server/apps/inm/models.py @@ -91,7 +91,7 @@ class IProduct(BaseModel): material = models.ForeignKey(Material, verbose_name='物料类型', on_delete=models.CASCADE) warehouse = models.ForeignKey(WareHouse, on_delete=models.CASCADE, verbose_name='所在仓库') batch = models.CharField('所属批次号', max_length=100, null=True, blank=True) - d_product = models.ForeignKey('wpm.product', on_delete=models.CASCADE, verbose_name='关联的动态产品', db_constraint=False, null=True, blank=True) + wproduct = models.ForeignKey('wpm.wproduct', on_delete=models.CASCADE, verbose_name='关联的动态产品', db_constraint=False, null=True, blank=True) fifos = models.JSONField('关联出入库记录', default=list, blank=True) diff --git a/hb_server/apps/inm/serializers.py b/hb_server/apps/inm/serializers.py index 9bce6f5..b0945ef 100644 --- a/hb_server/apps/inm/serializers.py +++ b/hb_server/apps/inm/serializers.py @@ -59,6 +59,11 @@ class FIFODetailInPurSerializer(serializers.ModelSerializer): model = FIFODetail fields = ['material', 'count', 'batch', 'details'] +class MaterialBatchQuerySerializer(serializers.Serializer): + warehouse = serializers.IntegerField(label="仓库ID", required=False) + materials = serializers.ListField(child=serializers.IntegerField(label="物料ID"), required=False) + + class FIFOInPurSerializer(serializers.ModelSerializer): """ 采购入库序列化 diff --git a/hb_server/apps/inm/signals.py b/hb_server/apps/inm/signals.py index b96b416..e638fd1 100644 --- a/hb_server/apps/inm/signals.py +++ b/hb_server/apps/inm/signals.py @@ -18,6 +18,7 @@ def update_by_fifodetail(sender, instance, created, **kwargs): o1.save() o2, _ = MaterialBatch.objects.get_or_create(material=material, warehouse=warehouse, batch=instance.batch,\ defaults={'material':material, 'warehouse':warehouse, 'count':0, 'batch':instance.batch}) + o2.count = o2.count + instance.count o2.save() material.count = material.count + 1 material.save() diff --git a/hb_server/apps/inm/views.py b/hb_server/apps/inm/views.py index 531be66..6bc66af 100644 --- a/hb_server/apps/inm/views.py +++ b/hb_server/apps/inm/views.py @@ -2,9 +2,10 @@ from django.shortcuts import render from rest_framework import serializers from rest_framework.mixins import ListModelMixin, RetrieveModelMixin from rest_framework.viewsets import GenericViewSet, ModelViewSet +from apps.inm.filters import MbFilterSet from apps.inm.models import FIFO, FIFODetail, MaterialBatch, WareHouse,Inventory -from apps.inm.serializers import FIFODetailSerializer, FIFOInPurSerializer, FIFOListSerializer, MaterialBatchSerializer, WareHouseSerializer, WareHouseCreateUpdateSerializer,InventorySerializer +from apps.inm.serializers import FIFODetailSerializer, FIFOInPurSerializer, FIFOListSerializer, MaterialBatchQuerySerializer, MaterialBatchSerializer, WareHouseSerializer, WareHouseCreateUpdateSerializer,InventorySerializer from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin from rest_framework.decorators import action from rest_framework.response import Response @@ -43,11 +44,22 @@ class MaterialBatchViewSet(ListModelMixin, GenericViewSet): perms_map = {'*': '*'} queryset = MaterialBatch.objects.select_related('material', 'warehouse').all() serializer_class = MaterialBatchSerializer - filterset_fields = ['material', 'warehouse'] + # filterset_fields = ['material', 'warehouse'] + filterset_class = MbFilterSet search_fields = [] ordering_fields = ['create_time'] ordering = ['-create_time'] + # @action(methods=['post'], detail=False, perms_map={'post':'*'}, serializer_class=MaterialBatchQuerySerializer) + # def query(self, request, pk=None): + # """ + # 复杂查询 + # """ + # serializer = MaterialBatchQuerySerializer(data=request.data) + # serializer.is_valid(raise_exception=True) + # queryset = self.queryset.filter() + # return Response() + class FIFODetailViewSet(ListModelMixin, GenericViewSet): """ 出入库记录详情表 @@ -66,11 +78,13 @@ class FIFOViewSet(ListModelMixin, GenericViewSet): """ perms_map = {'*': '*'} queryset = FIFO.objects.select_related('warehouse', 'operator') + serializer_class = FIFOListSerializer filterset_fields = ['warehouse'] def get_serializer_class(self): if self.action == 'list': return FIFOListSerializer + return super().get_serializer_class() @action(methods=['post'], detail=False, perms_map={'post':'*'}, serializer_class=FIFOInPurSerializer) def in_pur(self, request, pk=None): diff --git a/hb_server/apps/pm/migrations/0009_auto_20211029_1017.py b/hb_server/apps/pm/migrations/0009_auto_20211029_1017.py new file mode 100644 index 0000000..4dccffd --- /dev/null +++ b/hb_server/apps/pm/migrations/0009_auto_20211029_1017.py @@ -0,0 +1,27 @@ +# Generated by Django 3.2.6 on 2021-10-29 02:17 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('pm', '0008_subproductionprogress_count_current'), + ] + + operations = [ + migrations.RemoveField( + model_name='subproductionprogress', + name='count_current', + ), + migrations.AddField( + model_name='subproductionplan', + name='is_picked', + field=models.BooleanField(default=False, verbose_name='是否已领料'), + ), + migrations.AlterField( + model_name='subproductionprogress', + name='count', + field=models.FloatField(verbose_name='应出入数'), + ), + ] diff --git a/hb_server/apps/pm/models.py b/hb_server/apps/pm/models.py index d2025f5..94939b3 100644 --- a/hb_server/apps/pm/models.py +++ b/hb_server/apps/pm/models.py @@ -49,6 +49,7 @@ class SubProductionPlan(CommonAModel): state = models.IntegerField('状态', default=0) start_date_real = models.DateField('实际开工日期', null=True, blank=True) end_date_real = models.DateField('实际完工日期', null=True, blank=True) + is_picked = models.BooleanField('是否已领料', default=False) class Meta: verbose_name = '子生产计划' verbose_name_plural = verbose_name @@ -64,6 +65,5 @@ class SubProductionProgress(BaseModel): subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='关联子生产计划', on_delete=models.CASCADE, related_name='progress_subplan') material = models.ForeignKey(Material, verbose_name='关联物料', on_delete=models.CASCADE) type = models.IntegerField('物料应用类型', default=1) - count = models.IntegerField('应出入数') + count = models.FloatField('应出入数') count_real = models.IntegerField('实际出入数', default=0) - count_current = models.IntegerField('当前数量', default=0) diff --git a/hb_server/apps/pm/serializers.py b/hb_server/apps/pm/serializers.py index 85d1286..51821b9 100644 --- a/hb_server/apps/pm/serializers.py +++ b/hb_server/apps/pm/serializers.py @@ -44,3 +44,6 @@ class SubProductionProgressSerializer(serializers.ModelSerializer): class Meta: model = SubProductionProgress fields = '__all__' + +class PickNeedSerializer(serializers.Serializer): + warehouse = serializers.IntegerField(label="仓库ID") diff --git a/hb_server/apps/pm/views.py b/hb_server/apps/pm/views.py index 4515c00..6e3f340 100644 --- a/hb_server/apps/pm/views.py +++ b/hb_server/apps/pm/views.py @@ -3,9 +3,11 @@ from rest_framework import serializers from rest_framework.views import APIView from apps.em.models import Equipment from apps.em.serializers import EquipmentSerializer +from apps.inm.models import MaterialBatch +from apps.inm.serializers import MaterialBatchSerializer from apps.mtm.models import InputMaterial, OutputMaterial, Step, SubProduction, UsedStep from apps.system.mixins import CreateUpdateModelAMixin -from apps.pm.serializers import GenSubPlanSerializer, ProductionPlanCreateFromOrderSerializer, ProductionPlanSerializer, ResourceCalListSerializer, ResourceCalSerializer, SubProductionPlanListSerializer, SubProductionPlanUpdateSerializer, SubProductionProgressSerializer +from apps.pm.serializers import GenSubPlanSerializer, PickNeedSerializer, ProductionPlanCreateFromOrderSerializer, ProductionPlanSerializer, ResourceCalListSerializer, ResourceCalSerializer, SubProductionPlanListSerializer, SubProductionPlanUpdateSerializer, SubProductionProgressSerializer from rest_framework.mixins import CreateModelMixin, ListModelMixin, UpdateModelMixin from apps.pm.models import ProductionPlan, SubProductionProgress, SubProductionPlan from rest_framework.viewsets import GenericViewSet, ModelViewSet @@ -78,9 +80,9 @@ class ProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, CreateModel workshop=i.process.workshop, process=i.process, create_by=request.user, steps = list(steps)) for m in InputMaterial.objects.filter(subproduction=i, is_deleted=False).order_by('sort'): - SubProductionProgress.objects.create(material=m.material, type=1, count=m.count, subproduction_plan=instance) + SubProductionProgress.objects.create(material=m.material, type=1, count=m.count*production_plan.count, subproduction_plan=instance) for m in OutputMaterial.objects.filter(subproduction=i, is_deleted=False).order_by('sort'): - SubProductionProgress.objects.create(material=m.material, type=2, count=m.count, subproduction_plan=instance) + SubProductionProgress.objects.create(material=m.material, type=2, count=m.count*production_plan.count, subproduction_plan=instance) production_plan.is_planed=True production_plan.save() return Response() @@ -92,6 +94,7 @@ class SubProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, UpdateMo perms_map = {'*': '*'} queryset = SubProductionPlan.objects.select_related('process', 'workshop') search_fields = [] + serializer_class = SubProductionPlanListSerializer filterset_fields = ['production_plan'] ordering_fields = ['process__number'] ordering = ['process__number'] @@ -101,7 +104,7 @@ class SubProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, UpdateMo return SubProductionPlanListSerializer elif self.action == 'update': return SubProductionPlanUpdateSerializer - return SubProductionPlanListSerializer + return super().get_serializer_class() @action(methods=['get'], detail=True, perms_map={'get':'*'}, serializer_class=SubProductionProgressSerializer) def progress(self, request, pk=None): @@ -137,15 +140,21 @@ class SubProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, UpdateMo return Response() raise APIException('计划状态有误') - @action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=serializers.Serializer) + @action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=PickNeedSerializer) def pick_need(self, request, pk=None): """ - 领料需求清单 + 领料需求清单/库存数 """ obj = self.get_object() instance = SubProductionProgress.objects.filter(subproduction_plan=obj, type=1) serializer = SubProductionProgressSerializer(instance=instance, many=True) - return Response(serializer.data) + need = serializer.data + materials = [] + for i in need: + materials.append(i['material']) + objs = MaterialBatch.objects.filter(warehouse=request.data['warehouse'], material__id__in=materials) + have = MaterialBatchSerializer(instance=objs, many=True).data + return Response({'need':need, 'have':have}) class ResourceViewSet(GenericViewSet): diff --git a/hb_server/apps/wpm/migrations/0001_initial.py b/hb_server/apps/wpm/migrations/0001_initial.py new file mode 100644 index 0000000..596bbed --- /dev/null +++ b/hb_server/apps/wpm/migrations/0001_initial.py @@ -0,0 +1,86 @@ +# Generated by Django 3.2.6 on 2021-10-29 02:19 + +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 = [ + ('pm', '0009_auto_20211029_1017'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('mtm', '0025_outputmaterial_is_main'), + ] + + operations = [ + migrations.CreateModel( + name='WProduct', + fields=[ + ('id', models.BigAutoField(auto_created=True, 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='删除标记')), + ('number', models.CharField(blank=True, max_length=50, null=True, unique=True, verbose_name='物品编号')), + ('act_state', models.IntegerField(default=0, verbose_name='进行状态')), + ('remark', models.CharField(blank=True, max_length=200, null=True, verbose_name='备注')), + ('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wproduct_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')), + ('m_state', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.material', verbose_name='所属物料状态')), + ('p_state', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mtm.step', verbose_name='所在步骤')), + ('parent', models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.CASCADE, to='wpm.wproduct', verbose_name='上一级')), + ('subproduction_plan', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pm.subproductionplan', verbose_name='关联子生产计划')), + ('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wproduct_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='WProductForm', + fields=[ + ('id', models.BigAutoField(auto_created=True, 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='删除标记')), + ('data', models.JSONField(blank=True, default=dict, verbose_name='记录的数据')), + ('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wproductform_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')), + ('record_form', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.recordform', verbose_name='所用表格')), + ('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wproductform_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='WProductFlow', + fields=[ + ('id', models.BigAutoField(auto_created=True, 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='删除标记')), + ('wproduct', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='wpm.wproduct', verbose_name='产品')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='WMaterial', + fields=[ + ('id', models.BigAutoField(auto_created=True, 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='删除标记')), + ('batch', models.CharField(blank=True, max_length=100, null=True, verbose_name='批次号')), + ('count', models.IntegerField(default=0, verbose_name='当前数量')), + ('material', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.material', verbose_name='关联物料')), + ('subproduction_plan', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pm.subproductionplan', verbose_name='关联子计划')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/hb_server/apps/wpm/migrations/__init__.py b/hb_server/apps/wpm/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hb_server/apps/wpm/models.py b/hb_server/apps/wpm/models.py index c3e2c6e..af2de44 100644 --- a/hb_server/apps/wpm/models.py +++ b/hb_server/apps/wpm/models.py @@ -8,9 +8,18 @@ from utils.model import SoftModel, BaseModel from simple_history.models import HistoricalRecords from apps.mtm.models import Material, Step, RecordForm -class Product(CommonAModel): +class WMaterial(BaseModel): """ - 产品(所有生产过程中出现过的) + 车间生产物料 + """ + subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='关联子计划', on_delete=models.CASCADE) + material = models.ForeignKey(Material, verbose_name='关联物料', on_delete=models.CASCADE) + batch = models.CharField('批次号', max_length=100, null=True, blank=True) + count = models.IntegerField('当前数量', default=0) + +class WProduct(CommonAModel): + """ + 半成品/成品 """ act_state_choices=( (0, '待执行'), @@ -25,7 +34,7 @@ class Product(CommonAModel): remark = models.CharField('备注', max_length=200, null=True, blank=True) subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='关联子生产计划', on_delete=models.CASCADE) -class ProductForm(CommonAModel): +class WProductForm(CommonAModel): """ 记录表格 """ @@ -33,8 +42,8 @@ class ProductForm(CommonAModel): data = models.JSONField('记录的数据', default=dict, blank=True) -class ProductFlow(BaseModel): +class WProductFlow(BaseModel): """ 产品流转日志 """ - product = models.ForeignKey(Product, verbose_name='产品', on_delete=models.CASCADE) \ No newline at end of file + wproduct = models.ForeignKey(WProduct, verbose_name='产品', on_delete=models.CASCADE) \ No newline at end of file diff --git a/hb_server/apps/wpm/serializers.py b/hb_server/apps/wpm/serializers.py index 401a24f..39a4542 100644 --- a/hb_server/apps/wpm/serializers.py +++ b/hb_server/apps/wpm/serializers.py @@ -1,6 +1,10 @@ from rest_framework import serializers from rest_framework.serializers import ModelSerializer +class PickDetailSerializer(serializers.Serializer): + material = serializers.IntegerField(label='物料ID') + batch = serializers.CharField(label='物料批次') + pick_count = serializers.IntegerField(label="领料数量") class PickSerializer(serializers.Serializer): - warehouse = serializers.IntegerField() \ No newline at end of file + warehouse = serializers.IntegerField(label="仓库ID") \ No newline at end of file