From c2b38f3f1e7026793c25d215d956783c3a4d8e1a Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 6 Dec 2021 10:00:04 +0800 Subject: [PATCH] =?UTF-8?q?=E9=94=80=E5=94=AE=E8=AE=B0=E5=BD=95=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/inm/filters.py | 12 +++- .../inm/migrations/0020_iproduct_is_saled.py | 18 +++++ hb_server/apps/inm/models.py | 1 + hb_server/apps/inm/serializers.py | 9 +++ hb_server/apps/inm/views.py | 6 +- .../sam/migrations/0005_auto_20211203_1501.py | 28 ++++++++ .../sam/migrations/0006_auto_20211206_0958.py | 65 +++++++++++++++++++ hb_server/apps/sam/models.py | 41 ++++++++++-- hb_server/apps/sam/serializers.py | 39 ++++++++++- hb_server/apps/sam/urls.py | 3 +- hb_server/apps/sam/views.py | 31 +++++++-- .../wf/migrations/0017_auto_20211203_1501.py | 23 +++++++ hb_server/apps/wf/models.py | 26 ++++---- 13 files changed, 273 insertions(+), 29 deletions(-) create mode 100644 hb_server/apps/inm/migrations/0020_iproduct_is_saled.py create mode 100644 hb_server/apps/sam/migrations/0005_auto_20211203_1501.py create mode 100644 hb_server/apps/sam/migrations/0006_auto_20211206_0958.py create mode 100644 hb_server/apps/wf/migrations/0017_auto_20211203_1501.py diff --git a/hb_server/apps/inm/filters.py b/hb_server/apps/inm/filters.py index c71c4fb..3f550a3 100644 --- a/hb_server/apps/inm/filters.py +++ b/hb_server/apps/inm/filters.py @@ -1,9 +1,17 @@ from django_filters import rest_framework as filters from apps.mtm.models import Material -from .models import MaterialBatch +from .models import IProduct, 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 + fields = ['material', 'warehouse'] + + +class IProductFilterSet(filters.FilterSet): + + order = filters.NumberFilter(field_name="wproduct__subproduction_plan__production_plan__order") + class Meta: + model = IProduct + fields = ['material', 'warehouse', 'batch', 'order'] \ No newline at end of file diff --git a/hb_server/apps/inm/migrations/0020_iproduct_is_saled.py b/hb_server/apps/inm/migrations/0020_iproduct_is_saled.py new file mode 100644 index 0000000..5ca02e7 --- /dev/null +++ b/hb_server/apps/inm/migrations/0020_iproduct_is_saled.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.9 on 2021-12-06 01:58 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('inm', '0019_auto_20211201_1011'), + ] + + operations = [ + migrations.AddField( + model_name='iproduct', + name='is_saled', + field=models.BooleanField(default=False, verbose_name='是否售出'), + ), + ] diff --git a/hb_server/apps/inm/models.py b/hb_server/apps/inm/models.py index 20d87dd..5c485d0 100644 --- a/hb_server/apps/inm/models.py +++ b/hb_server/apps/inm/models.py @@ -102,5 +102,6 @@ class IProduct(BaseModel): warehouse = models.ForeignKey(WareHouse, on_delete=models.CASCADE, verbose_name='所在仓库') batch = models.CharField('所属批次号', max_length=100, default='') wproduct = models.ForeignKey('wpm.wproduct', on_delete=models.CASCADE, verbose_name='关联的动态产品', db_constraint=False, null=True, blank=True) + is_saled = models.BooleanField('是否售出', default=False) diff --git a/hb_server/apps/inm/serializers.py b/hb_server/apps/inm/serializers.py index 7f050fc..0a44f79 100644 --- a/hb_server/apps/inm/serializers.py +++ b/hb_server/apps/inm/serializers.py @@ -2,6 +2,7 @@ from rest_framework import serializers from apps.inm.models import FIFO, FIFOItem, FIFOItemProduct, IProduct, MaterialBatch, WareHouse,Inventory from apps.qm.models import TestRecord, TestRecordItem +from apps.sam.serializers import OrderSimpleSerializer from apps.system.serializers import UserSimpleSerializer from apps.mtm.serializers import MaterialSimpleSerializer @@ -40,10 +41,18 @@ class MaterialBatchSerializer(serializers. ModelSerializer): class IProductListSerializer(serializers.ModelSerializer): material_= MaterialSimpleSerializer(source='material', read_only=True) warehouse_ = WareHouseSimpleSerializer(source='warehouse', read_only=True) + order_ = serializers.SerializerMethodField() class Meta: model = IProduct fields = '__all__' + def get_order_(self, obj): + if obj.wproduct: + order = obj.wproduct.subproduction_plan.production_plan.order + if order: + return OrderSimpleSerializer(instance=order).data + return None + class FIFOListSerializer(serializers.ModelSerializer): auditor_ = UserSimpleSerializer(source='auditor', read_only=True) diff --git a/hb_server/apps/inm/views.py b/hb_server/apps/inm/views.py index 6454c40..8f455e0 100644 --- a/hb_server/apps/inm/views.py +++ b/hb_server/apps/inm/views.py @@ -3,7 +3,7 @@ from rest_framework import serializers from rest_framework.exceptions import APIException from rest_framework.mixins import DestroyModelMixin, ListModelMixin, RetrieveModelMixin from rest_framework.viewsets import GenericViewSet, ModelViewSet -from apps.inm.filters import MbFilterSet +from apps.inm.filters import IProductFilterSet, MbFilterSet from apps.inm.models import FIFO, FIFOItem, IProduct, MaterialBatch, WareHouse,Inventory from apps.inm.serializers import FIFOItemSerializer, FIFOInPurSerializer, FIFOListSerializer, IProductListSerializer, InmTestRecordCreateSerializer, MaterialBatchQuerySerializer, MaterialBatchSerializer, WareHouseSerializer, WareHouseCreateUpdateSerializer,InventorySerializer @@ -167,9 +167,9 @@ class IProductViewSet(ListModelMixin, GenericViewSet): 半成品库存表 """ perms_map = {'*': '*'} - queryset = IProduct.objects.select_related('material', 'warehouse').all() + queryset = IProduct.objects.select_related('material', 'warehouse', 'wproduct__subproduction_plan__production_plan__order').filter(is_saled=False) serializer_class = IProductListSerializer - filterset_fields = ['material', 'warehouse', 'batch'] + filterset_class = IProductFilterSet search_fields = [] ordering_fields = ['create_time'] ordering = ['-create_time'] \ No newline at end of file diff --git a/hb_server/apps/sam/migrations/0005_auto_20211203_1501.py b/hb_server/apps/sam/migrations/0005_auto_20211203_1501.py new file mode 100644 index 0000000..c819554 --- /dev/null +++ b/hb_server/apps/sam/migrations/0005_auto_20211203_1501.py @@ -0,0 +1,28 @@ +# Generated by Django 3.2.9 on 2021-12-03 07:01 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('sam', '0004_order_planed_count'), + ] + + operations = [ + migrations.AddField( + model_name='order', + name='delivered_count', + field=models.PositiveIntegerField(default=0, verbose_name='交货数量'), + ), + migrations.AlterField( + model_name='order', + name='count', + field=models.PositiveIntegerField(default=0, verbose_name='所需数量'), + ), + migrations.AlterField( + model_name='order', + name='planed_count', + field=models.PositiveIntegerField(default=0, verbose_name='已排数量'), + ), + ] diff --git a/hb_server/apps/sam/migrations/0006_auto_20211206_0958.py b/hb_server/apps/sam/migrations/0006_auto_20211206_0958.py new file mode 100644 index 0000000..9b99454 --- /dev/null +++ b/hb_server/apps/sam/migrations/0006_auto_20211206_0958.py @@ -0,0 +1,65 @@ +# Generated by Django 3.2.9 on 2021-12-06 01:58 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('inm', '0020_iproduct_is_saled'), + ('mtm', '0041_alter_material_type'), + ('sam', '0005_auto_20211203_1501'), + ] + + operations = [ + migrations.AlterField( + model_name='order', + name='delivered_count', + field=models.PositiveIntegerField(default=0, verbose_name='已交货数量'), + ), + migrations.AlterField( + model_name='order', + name='number', + field=models.CharField(max_length=100, unique=True, verbose_name='订单编号'), + ), + migrations.CreateModel( + name='Sale', + 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='删除标记')), + ('count', models.PositiveIntegerField(default=0, verbose_name='交货数量')), + ('is_audited', models.BooleanField(default=False, verbose_name='是否审核')), + ('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sale_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')), + ('customer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sam.customer', verbose_name='客户')), + ('order', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='sam.order', verbose_name='关联订单')), + ('product', 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='sale_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='SaleProduct', + 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(max_length=50, unique=True, verbose_name='物品编号')), + ('is_mtested', models.BooleanField(default=False, verbose_name='是否军检')), + ('is_mtestok', models.BooleanField(default=True, verbose_name='是否军检合格')), + ('iproduct', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='sale_iproduct', to='inm.iproduct', verbose_name='关联库存产品')), + ('sale', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sam.sale', verbose_name='关联销售记录')), + ], + options={ + 'unique_together': {('sale', 'iproduct')}, + }, + ), + ] diff --git a/hb_server/apps/sam/models.py b/hb_server/apps/sam/models.py index 583fe87..631a472 100644 --- a/hb_server/apps/sam/models.py +++ b/hb_server/apps/sam/models.py @@ -1,4 +1,4 @@ -from apps.system.models import CommonAModel +from apps.system.models import CommonADModel, CommonAModel from django.db import models from django.contrib.auth.models import AbstractUser from django.db.models.base import Model @@ -58,16 +58,47 @@ class Order(CommonAModel): """ 订单信息 """ - number = models.CharField('订单编号', max_length=100) + number = models.CharField('订单编号', max_length=100, unique=True) customer = models.ForeignKey(Customer, verbose_name='客户', on_delete=models.CASCADE) contract = models.ForeignKey(Contract, verbose_name='所属合同', null=True, blank=True, on_delete=models.SET_NULL) product = models.ForeignKey(Material, verbose_name='所需产品', on_delete=models.CASCADE) - count = models.IntegerField('所需数量', default=0) - planed_count = models.IntegerField('已排数量', default=0) + count = models.PositiveIntegerField('所需数量', default=0) + planed_count = models.PositiveIntegerField('已排数量', default=0) + delivered_count = models.PositiveIntegerField('已交货数量', default=0) delivery_date = models.DateField('交货日期') class Meta: verbose_name = '订单信息' verbose_name_plural = verbose_name def __str__(self): - return self.name \ No newline at end of file + return self.name + + +class Sale(CommonADModel): + """ + 销售记录 + """ + customer = models.ForeignKey(Customer, verbose_name='客户', on_delete=models.CASCADE) + order = models.ForeignKey(Order, verbose_name='关联订单', on_delete=models.CASCADE, null=True, blank=True) + product = models.ForeignKey(Material, verbose_name='所需产品', on_delete=models.CASCADE) + count = models.PositiveIntegerField('交货数量', default=0) + is_audited = models.BooleanField('是否审核', default=False) + + +class SaleProduct(BaseModel): + """ + 具体产品 + """ + sale = models.ForeignKey(Sale, verbose_name='关联销售记录', on_delete=models.CASCADE) + number = models.CharField('物品编号', unique=True, max_length=50) + iproduct = models.ForeignKey('inm.iproduct', verbose_name='关联库存产品', on_delete=models.CASCADE, related_name='sale_iproduct') + is_mtested = models.BooleanField('是否军检', default=False) + is_mtestok = models.BooleanField('是否军检合格', default=True) + + class Meta: + unique_together = ( + ('sale','iproduct'), # 联合唯一 + ) + + + diff --git a/hb_server/apps/sam/serializers.py b/hb_server/apps/sam/serializers.py index a4ed099..320ceed 100644 --- a/hb_server/apps/sam/serializers.py +++ b/hb_server/apps/sam/serializers.py @@ -1,6 +1,8 @@ from rest_framework import serializers -from .models import Contract, Customer, Order +from apps.inm.models import IProduct + +from .models import Contract, Customer, Order, Sale, SaleProduct from apps.mtm.serializers import MaterialSimpleSerializer @@ -54,3 +56,38 @@ class OrderSimpleSerializer(serializers.ModelSerializer): class Meta: model = Order fields = '__all__' + +class SaleCreateSerializer(serializers.ModelSerializer): + iproducts = serializers.PrimaryKeyRelatedField(queryset=IProduct.objects.all(), many=True) + class Meta: + model = Sale + fields = ['customer', 'order', 'product', 'iproducts'] + + def validate(self, attrs): + order = attrs.get('order', None) + if order: + if order.customer: + attrs['customer'] = order.customer + attrs['product'] = order.product + return super().validate(attrs) + + def create(self, validated_data): + iproducts = validated_data.pop('iproducts') + sale = Sale.objects.create(**validated_data) + i_l = [] + for i in iproducts: + i_d ={} + i_d['sale'] = sale + i_d['number'] = i.number + i_d['iproduct'] = i + i_l.append(SaleProduct(**i_d)) + SaleProduct.objects.bulk_create(i_l) + return sale + +class SaleListSerializer(serializers.ModelSerializer): + customer_ = CustomerSimpleSerializer(source='customer', read_only=True) + order_ = OrderSimpleSerializer(source='order', read_only=True) + product_ = MaterialSimpleSerializer(source='product', read_only=True) + class Meta: + model = Sale + fields = '__all__' diff --git a/hb_server/apps/sam/urls.py b/hb_server/apps/sam/urls.py index 4aa607f..2418046 100644 --- a/hb_server/apps/sam/urls.py +++ b/hb_server/apps/sam/urls.py @@ -1,6 +1,6 @@ from django.db.models import base from rest_framework import urlpatterns -from apps.sam.views import CustomerViewSet,ContractViewSet,OrderViewSet +from apps.sam.views import CustomerViewSet,ContractViewSet,OrderViewSet, SaleViewSet from django.urls import path, include from rest_framework.routers import DefaultRouter @@ -8,6 +8,7 @@ router = DefaultRouter() router.register('customer', CustomerViewSet, basename='customer') router.register('contract', ContractViewSet, basename='contract') router.register('order', OrderViewSet, basename='order') +router.register('sale', SaleViewSet, basename='sale') urlpatterns = [ path('', include(router.urls)), diff --git a/hb_server/apps/sam/views.py b/hb_server/apps/sam/views.py index 441e7fa..98bced6 100644 --- a/hb_server/apps/sam/views.py +++ b/hb_server/apps/sam/views.py @@ -1,6 +1,7 @@ -from apps.sam.serializers import ContractCreateUpdateSerializer, ContractSerializer, CustomerCreateUpdateSerializer, CustomerSerializer, OrderCreateUpdateSerializer, OrderSerializer -from apps.sam.models import Contract, Customer, Order -from rest_framework.viewsets import ModelViewSet +from rest_framework.mixins import CreateModelMixin, ListModelMixin, RetrieveModelMixin +from apps.sam.serializers import ContractCreateUpdateSerializer, ContractSerializer, CustomerCreateUpdateSerializer, CustomerSerializer, OrderCreateUpdateSerializer, OrderSerializer, SaleCreateSerializer, SaleListSerializer +from apps.sam.models import Contract, Customer, Order, Sale +from rest_framework.viewsets import GenericViewSet, ModelViewSet from apps.system.mixins import CreateUpdateCustomMixin from django.shortcuts import render from rest_framework.decorators import action @@ -57,7 +58,7 @@ class OrderViewSet(CreateUpdateCustomMixin, ModelViewSet): def get_serializer_class(self): if self.action in ['create', 'update']: return OrderCreateUpdateSerializer - return OrderSerializer + return super().get_serializer_class() @action(methods=['get'], detail=False, perms_map={'get':'*'}) def toplan(self, request, pk=None): @@ -67,4 +68,24 @@ class OrderViewSet(CreateUpdateCustomMixin, ModelViewSet): serializer = self.get_serializer(page, many=True) return self.get_paginated_response(serializer.data) serializer = self.get_serializer(queryset, many=True) - return Response(serializer.data) \ No newline at end of file + return Response(serializer.data) + + +class SaleViewSet(CreateUpdateCustomMixin, ListModelMixin, RetrieveModelMixin, CreateModelMixin, GenericViewSet): + """ + 销售记录 + """ + perms_map = {'*': '*'} + queryset = Sale.objects.select_related('customer', 'order', 'product').all() + serializer_class = SaleListSerializer + search_fields = ['customer__name', 'order__number'] + filterset_fields = ['product', 'order', 'customer'] + ordering_fields = ['create_time'] + ordering = ['-create_time'] + + def get_serializer_class(self): + if self.action == 'create': + return SaleCreateSerializer + elif self.action == 'retrieve': + return SaleListSerializer + return super().get_serializer_class() \ No newline at end of file diff --git a/hb_server/apps/wf/migrations/0017_auto_20211203_1501.py b/hb_server/apps/wf/migrations/0017_auto_20211203_1501.py new file mode 100644 index 0000000..f5b3766 --- /dev/null +++ b/hb_server/apps/wf/migrations/0017_auto_20211203_1501.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2.9 on 2021-12-03 07:01 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('wf', '0016_auto_20211024_2349'), + ] + + operations = [ + migrations.AlterField( + model_name='customfield', + name='field_type', + field=models.CharField(choices=[('string', '字符串'), ('int', '整型'), ('float', '浮点'), ('boolean', '布尔'), ('date', '日期'), ('datetime', '日期时间'), ('radio', '单选'), ('checkbox', '多选'), ('select', '单选下拉'), ('selects', '多选下拉'), ('textarea', '文本域'), ('selectuser', '单选用户'), ('selectusers', '多选用户'), ('file', '附件'), ('draw', '绘图')], help_text='5.字符串,10.整形,15.浮点型,20.布尔,25.日期,30.日期时间,35.单选框,40.多选框,45.下拉列表,50.多选下拉列表,55.文本域,60.用户名, 70.多选的用户名, 80.附件(只保存路径,多个使用逗号隔开)', max_length=50, verbose_name='类型'), + ), + migrations.AlterField( + model_name='ticket', + name='ticket_data', + field=models.JSONField(default=dict, help_text='工单自定义字段内容', verbose_name='工单数据'), + ), + ] diff --git a/hb_server/apps/wf/models.py b/hb_server/apps/wf/models.py index a9ae071..bc1597c 100644 --- a/hb_server/apps/wf/models.py +++ b/hb_server/apps/wf/models.py @@ -152,7 +152,8 @@ class CustomField(CommonAModel): ('textarea', '文本域'), ('selectuser', '单选用户'), ('selectusers', '多选用户'), - ('file', '附件') + ('file', '附件'), + ('draw', '绘图') ) workflow = models.ForeignKey(Workflow, on_delete=models.CASCADE, verbose_name='所属工作流') field_type = models.CharField('类型', max_length=50, choices=field_type_choices, help_text='5.字符串,10.整形,15.浮点型,20.布尔,25.日期,30.日期时间,35.单选框,40.多选框,45.下拉列表,50.多选下拉列表,55.文本域,60.用户名, 70.多选的用户名, 80.附件(只保存路径,多个使用逗号隔开)') @@ -201,7 +202,7 @@ class Ticket(CommonBModel): state = models.ForeignKey(State, on_delete=models.CASCADE, verbose_name='当前状态', related_name='ticket_state') parent = models.ForeignKey('self', null=True, blank=True, on_delete=models.CASCADE, verbose_name='父工单') parent_state = models.ForeignKey(State, null=True, blank=True, on_delete=models.CASCADE, verbose_name='父工单状态', related_name='ticket_parent_state') - ticket_data = models.JSONField('工单数据', default=dict, help_text='工单所有字段内容') + ticket_data = models.JSONField('工单数据', default=dict, help_text='工单自定义字段内容') in_add_node = models.BooleanField('加签状态中', default=False, help_text='是否处于加签状态下') add_node_man = models.ForeignKey(User, verbose_name='加签人', on_delete=models.SET_NULL, null=True, blank=True, help_text='加签操作的人,工单当前处理人处理完成后会回到该处理人,当处于加签状态下才有效') @@ -211,16 +212,17 @@ class Ticket(CommonBModel): multi_all_person = models.JSONField('全部处理的结果', default=dict, blank=True, help_text='需要当前状态处理人全部处理时实际的处理结果,json格式') -class TicketData(): - """ - 工单数据,自定义字段值 - """ - form_field = models.ForeignKey(CustomField, verbose_name='关联字段', on_delete=models.SET_NULL, db_constraint=False, null=True, blank=True) - field_name = models.CharField('字段名', max_length=50) - field_key = models.CharField('字段标识', max_length=50) - field_type = models.CharField('字段类型', choices=CustomField.field_type_choices, max_length=50) - field_value = models.JSONField('录入值', default=dict, blank=True) - sort = models.IntegerField('排序号', default=1) +# class TicketCustomField(BaseModel): +# """ +# 工单数据,自定义字段值 +# """ +# ticket = models.ForeignKey(Ticket, verbose_name='关联工单', on_delete=models.CASCADE) +# form_field = models.ForeignKey(CustomField, verbose_name='关联字段', on_delete=models.SET_NULL, db_constraint=False, null=True, blank=True) +# field_name = models.CharField('字段名', max_length=50) +# field_key = models.CharField('字段标识', max_length=50) +# field_type = models.CharField('字段类型', choices=CustomField.field_type_choices, max_length=50) +# field_value = models.JSONField('录入值', default=dict, blank=True) +# sort = models.IntegerField('排序号', default=1) class TicketFlow(BaseModel): """