From b778da06f1d9abf804538b63da112e376a89efc6 Mon Sep 17 00:00:00 2001 From: laoxiao Date: Sat, 1 Apr 2023 21:59:26 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=87=E8=B4=AD=E8=AE=A2=E5=8D=95=E7=9A=84?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E5=AE=8C=E6=88=90=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/vcs.xml | 6 + .../purchase_info/serializers/__init__.py | 0 .../serializers/purchase_serializer.py | 72 ++++++++++++ ERP_5/apps/purchase_info/urls.py | 4 +- ERP_5/apps/purchase_info/views.py | 3 - ERP_5/apps/purchase_info/views/__init__.py | 0 .../apps/purchase_info/views/purchase_view.py | 106 ++++++++++++++++++ ERP_5/utils/base_views.py | 43 +++++++ 8 files changed, 229 insertions(+), 5 deletions(-) create mode 100644 .idea/vcs.xml create mode 100644 ERP_5/apps/purchase_info/serializers/__init__.py create mode 100644 ERP_5/apps/purchase_info/serializers/purchase_serializer.py delete mode 100644 ERP_5/apps/purchase_info/views.py create mode 100644 ERP_5/apps/purchase_info/views/__init__.py create mode 100644 ERP_5/apps/purchase_info/views/purchase_view.py diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/ERP_5/apps/purchase_info/serializers/__init__.py b/ERP_5/apps/purchase_info/serializers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ERP_5/apps/purchase_info/serializers/purchase_serializer.py b/ERP_5/apps/purchase_info/serializers/purchase_serializer.py new file mode 100644 index 0000000..c56ac45 --- /dev/null +++ b/ERP_5/apps/purchase_info/serializers/purchase_serializer.py @@ -0,0 +1,72 @@ +from django.db import transaction +from rest_framework import serializers +from rest_framework.exceptions import ValidationError + +from ERP_5.utils.get_inventory import get_inventory_by_goods +from purchase_info.models import PurchaseModel, PurchaseItemModel + + +class PurchaseItemsSerializer(serializers.ModelSerializer): + """ + 采购单中的项目 的序列化器 + """ + model_number = serializers.CharField(source='goods.model_number', read_only=True) + color = serializers.CharField(source='goods.color', read_only=True) + nits_name = serializers.CharField(source='goods.units.basic_name', read_only=True) + cur_inventory = serializers.SerializerMethodField(read_only=True) + + class Meta: + model = PurchaseItemModel + fields = '__all__' + + def get_cur_inventory(self, obj): + return get_inventory_by_goods(obj.goods.id) + + +class PurchaseSerializer(serializers.ModelSerializer): + """ + 采购单的序列化器 + """ + # 采购单列表中需要展示的商品信息: + goods_info = serializers.SerializerMethodField(read_only=True) + # item_list 用于新增,修改,查询 + item_list = PurchaseItemsSerializer(many=True) + + class Meta: + model = PurchaseModel + fields = '__all__' + + def create(self, validated_data): + item_list = validated_data.pop('item_list') + with transaction.atomic(): + purchase = PurchaseModel.objects.create(**validated_data) + for item in item_list: + PurchaseItemModel.objects.create(purchase=purchase, **item) + return purchase + + # 同时插入采购单和采购单中货品项。必须重写update + def update(self, instance, validated_data): + if instance.status != '0': + raise ValidationError("采购单已经生效,不能修改!") + item_list = validated_data.pop('item_list') + old_list = instance.item_list.all() + with transaction.atomic(): + if old_list.exists(): + # 然后把旧数据删除,因为在validated_data拿不到货品库存数据的ID + instance.item_list.all().delete() + + for item in item_list: # 重新插入采购项 数据 + PurchaseItemModel.objects.create(purchase=instance, **item) + result = super(PurchaseSerializer, self).update(instance=instance, validated_data=validated_data) + return result + + def get_goods_info(self, obj): + """ + 商品信息是由: 商品1名称 商品1规格 , 商品2名称 商品2规格 ,.... + """ + if obj.item_list.all(): + result = [] + for item in obj.item_list.all(): + result.append(item.name + (item.specification if item.specification else '')) + return ', '.join(result) + return "" diff --git a/ERP_5/apps/purchase_info/urls.py b/ERP_5/apps/purchase_info/urls.py index 7a3b23f..553e739 100644 --- a/ERP_5/apps/purchase_info/urls.py +++ b/ERP_5/apps/purchase_info/urls.py @@ -16,7 +16,7 @@ Including another URLconf from django.contrib import admin from django.urls import path, re_path from rest_framework.routers import DefaultRouter -# from .views import units_views, attachments_views, goods_category_views, goods_views +from .views import purchase_view from rest_framework_jwt.views import obtain_jwt_token @@ -26,6 +26,6 @@ urlpatterns = [ ] router = DefaultRouter() -# router.register('category', goods_category_views.CategoryView) +router.register('purchases', purchase_view.PurchaseViewSet) urlpatterns += router.urls diff --git a/ERP_5/apps/purchase_info/views.py b/ERP_5/apps/purchase_info/views.py deleted file mode 100644 index 91ea44a..0000000 --- a/ERP_5/apps/purchase_info/views.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.shortcuts import render - -# Create your views here. diff --git a/ERP_5/apps/purchase_info/views/__init__.py b/ERP_5/apps/purchase_info/views/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ERP_5/apps/purchase_info/views/purchase_view.py b/ERP_5/apps/purchase_info/views/purchase_view.py new file mode 100644 index 0000000..45a7795 --- /dev/null +++ b/ERP_5/apps/purchase_info/views/purchase_view.py @@ -0,0 +1,106 @@ +from django.db.models import Q +from drf_yasg import openapi +from drf_yasg.utils import swagger_auto_schema +from rest_framework import viewsets +from rest_framework.decorators import action + +from ERP_5.utils.base_views import MultipleDestroyMixin, MultipleAuditMixin +from ERP_5.utils.paginations import GlobalPagination +from purchase_info.models import PurchaseModel +from purchase_info.serializers.purchase_serializer import PurchaseSerializer + + +class PurchaseViewSet(viewsets.ModelViewSet, MultipleDestroyMixin, MultipleAuditMixin): + """ + create: + 采购单--新增,注意:其中images_list="1,2,3,4";里面是附件的ID + + 采购单新增, status: 201(成功), return: 新增采购单信息 + + destroy: + 采购单--删除 + + 采购单删除, status: 204(成功), return: None + + multiple_delete: + 采购单--批量删除,必传参数:ids=[1,2,3,4...] + + 采购单批量删除, status: 204(成功), return: None + + update: + 采购单--修改,注意:其中images_list="1,2,3,4";里面是附件的ID + + 采购单修改, status: 200(成功), return: 修改后的采购单信息 + + partial_update: + 采购单--局部修改,可以传参任意属性的值,服务器会修改指定的属性值 + + 采购单局部修改, status: 200(成功), return: 修改后的采购单信息 + + list: + 采购单--该接口可以弃用 + + 采购单列表信息, status: 200(成功), return: 采购单信息列表 + + retrieve: + 查询某一个采购单 + + 查询指定ID的采购单, status: 200(成功), return: 用户采购单 + """ + + queryset = PurchaseModel.objects.all() + serializer_class = PurchaseSerializer + pagination_class = GlobalPagination + + def get_queryset(self): + if self.action == 'find': # 过滤查询 + # 获取请求参数(在json中): + number_code = self.request.data.get('number_code', None) + keyword = self.request.data.get('keyword', None) + start_date = self.request.data.get('start_date', None) + end_date = self.request.data.get('start_date', None) + supplier = self.request.data.get('supplier', 0) + operator_user = self.request.data.get('operator_user', 0) + status = self.request.data.get('status', None) + query = Q() + if keyword: + child_query = Q() + child_query.add(Q(item_list__name__contains=keyword), 'OR') + child_query.add(Q(item_list__specification=keyword), 'OR') + query.add(child_query, 'AND') + if start_date: + query.add(Q(invoices_date__gt=start_date), 'AND') + if end_date: + query.add(Q(invoices_date__lt=end_date), 'AND') + + if supplier: + query.add(Q(supplier__id=supplier), 'AND') + if number_code: + query.add(Q(number_code__contains=number_code), 'AND') + if operator_user: + query.add(Q(operator_user__id=operator_user), 'AND') + if status: + query.add(Q(status=status), 'AND') + + return PurchaseModel.objects.filter(query).distinct().all() + else: + return PurchaseModel.objects.all() + + params = openapi.Schema(type=openapi.TYPE_OBJECT, properties={ + 'keyword': openapi.Schema(type=openapi.TYPE_STRING, description="名称的关键字或者型号"), + 'start_date': openapi.Schema(type=openapi.TYPE_STRING, description="起始日期2020-10-01"), + 'number_code': openapi.Schema(type=openapi.TYPE_STRING, description="编号(序列号)"), + 'end_date': openapi.Schema(type=openapi.TYPE_STRING, description="结束日期2020-10-01"), + 'status': openapi.Schema(type=openapi.TYPE_STRING, description="状态0或者1,2,3.."), + 'supplier': openapi.Schema(type=openapi.TYPE_INTEGER, description="供应商的ID"), + 'operator_user': openapi.Schema(type=openapi.TYPE_INTEGER, description="操作用户的ID"), + }) + # 分页参数必须是query_param(看源码) + page_param = openapi.Parameter(name='page', in_=openapi.IN_QUERY, description="页号", type=openapi.TYPE_INTEGER) + size_param = openapi.Parameter(name='size', in_=openapi.IN_QUERY, description="每页显示数量", type=openapi.TYPE_INTEGER) + + @swagger_auto_schema(method='POST', request_body=params, manual_parameters=[page_param, size_param], + operation_description="采购订单的搜索过滤") + @action(methods=['POST'], detail=False) + def find(self, request, *args, **kwargs): + return super(PurchaseViewSet, self).list(request=request, *args, **kwargs) diff --git a/ERP_5/utils/base_views.py b/ERP_5/utils/base_views.py index 23e99f6..8112ed1 100644 --- a/ERP_5/utils/base_views.py +++ b/ERP_5/utils/base_views.py @@ -6,6 +6,7 @@ from rest_framework.response import Response from ERP_5.utils.cont import NumberPrefix from ERP_5.utils.generate_code import generate_code +from erp_system.models import UserModel class MultipleDestroyMixin: @@ -83,3 +84,45 @@ class GenerateCode(views.APIView): return Response(data={'detail': 'prefix没有配置,参考cont.py'}, status=status.HTTP_400_BAD_REQUEST) else: return Response(data={'detail': 'prefix没有,该参数必须传'}, status=status.HTTP_400_BAD_REQUEST) + + +class MultipleAuditMixin: + """ + 自定义的 批量审核(反审核)的 视图函数 + """ + + body_param = openapi.Schema(type=openapi.TYPE_OBJECT, required=['ids', 'user_id'], properties={ + 'ids': openapi.Schema(type=openapi.TYPE_ARRAY, items=openapi.Schema(type=openapi.TYPE_INTEGER), + description="选择哪些需要批量的ID(主键)列表"), + 'user_id': openapi.Schema(type=openapi.TYPE_INTEGER, description="审核人的用户ID"), + 'is_audit': openapi.Schema(type=openapi.TYPE_STRING, description="是否审核,审核:1, 反审核:0") + }) + + @swagger_auto_schema(method='put', request_body=body_param, operation_description="批量审核或者 反审核") + @action(methods=['put'], detail=False) + def multiple_audit(self, request, *args, **kwargs): + audit_ids = request.data.get('ids') + user_id = request.data.get('user_id') # 用户ID + is_audit = request.data.get('is_audit', '1') # 是否审核,审核:1, 反审核:0 + + if not audit_ids: + return Response(data={'detail': '参数错误,ids为必传参数'}, status=status.HTTP_400_BAD_REQUEST) + if not isinstance(audit_ids, list): + return Response(data={'detail': 'ids格式错误,必须为List'}, status=status.HTTP_400_BAD_REQUEST) + queryset = self.get_queryset() + del_queryset = queryset.filter(id__in=audit_ids) + if len(audit_ids) != del_queryset.count(): + return Response(data={'detail': '数据不存在'}, status=status.HTTP_400_BAD_REQUEST) + for item in del_queryset.all(): + if item.status != '1' and is_audit == '0': # 1以后的状态是不能反审核的 + return Response(data={'detail': '不能反审核,因为订单已生效'}, status=status.HTTP_400_BAD_REQUEST) + if item.status != '0' and is_audit == '1': # 0以后的状态是不能审核的 + return Response(data={'detail': '不能审核,因为订单已生效'}, status=status.HTTP_400_BAD_REQUEST) + + + if not user_id: + return Response(data={'detail': '参数错误,user_id为必传参数'}, status=status.HTTP_400_BAD_REQUEST) + check_user = UserModel.objects.get(id=int(user_id)) + + del_queryset.update(status=is_audit, check_user_name=check_user.real_name, check_user_id=check_user.id) + return Response(status=status.HTTP_200_OK) \ No newline at end of file