From 42bb5af691c7bb0d0446d92d974f16333d0c79fb Mon Sep 17 00:00:00 2001 From: Czw996 <459749926@qq.com> Date: Mon, 20 Dec 2021 17:49:41 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=9F=BA=E7=A1=80=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=AF=BC=E5=85=A5=E5=AF=BC=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/data/models.py | 2 +- apps/data/serializers.py | 177 ++++++++++++++++++++++++++++++++----- apps/data/views.py | 164 ++++++++++++++++++++++++++++++++++ apps/goods/models.py | 2 +- apps/goods/serializers.py | 89 +++++++++++++++++-- apps/goods/views.py | 41 +++++++++ apps/system/models.py | 2 +- apps/system/serializers.py | 8 ++ 8 files changed, 454 insertions(+), 31 deletions(-) diff --git a/apps/data/models.py b/apps/data/models.py index 9a05643..312ee1a 100644 --- a/apps/data/models.py +++ b/apps/data/models.py @@ -150,7 +150,7 @@ class Account(Model): number = CharField(max_length=32, verbose_name='编号') name = CharField(max_length=64, verbose_name='名称') - type = CharField(max_length=32, choices=Type.choices, verbose_name='账户类型') + type = CharField(max_length=32, choices=Type.choices, default=Type.CASH, verbose_name='账户类型') holder = CharField(max_length=64, null=True, blank=True, verbose_name='开户人') remark = CharField(max_length=256, null=True, blank=True, verbose_name='备注') order = IntegerField(default=100, verbose_name='排序') diff --git a/apps/data/serializers.py b/apps/data/serializers.py index 9bf6f87..ab81fec 100644 --- a/apps/data/serializers.py +++ b/apps/data/serializers.py @@ -30,10 +30,9 @@ class WarehouseSerializer(BaseSerializer): class WarehouseExportSerializer(BaseSerializer): - number = CharField(label='仓库编号') - name = CharField(label='仓库名称') - manager_username = CharField(source='manager.username', label='管理员用户名') - manager_name = CharField(source='manager.name', label='管理员名称') + number = CharField(label='编号') + name = CharField(label='名称') + manager_name = CharField(source='manager.name', label='管理员') phone = CharField(label='电话') address = CharField(label='地址') remark = CharField(label='备注') @@ -42,14 +41,14 @@ class WarehouseExportSerializer(BaseSerializer): class Meta: model = Warehouse - fields = ['number', 'name', 'manager_username', 'manager_name', 'phone', 'address', - 'remark', 'order', 'is_active'] + fields = ['number', 'name', 'manager_name', 'phone', 'address', 'remark', + 'order', 'is_active'] class WarehouseImportSerializer(BaseSerializer): - number = CharField(label='仓库编号(必填)') - name = CharField(label='仓库名称(必填)') - manager_username = CharField(required=False, label='管理员用户名') + number = CharField(label='编号(必填)') + name = CharField(label='名称(必填)') + manager_name = CharField(required=False, label='管理员') phone = CharField(required=False, label='电话') address = CharField(required=False, label='地址') remark = CharField(required=False, label='备注') @@ -58,18 +57,16 @@ class WarehouseImportSerializer(BaseSerializer): class Meta: model = Warehouse - fields = ['number', 'name', 'manager_username', 'phone', 'address', 'remark', + fields = ['number', 'name', 'manager_name', 'phone', 'address', 'remark', 'order', 'is_active'] def validate(self, attrs): - if manager_username := attrs.pop('manager_username', None): - manager = User.objects.filter(username=manager_username, team=self.team).first() + if manager_name := attrs.pop('manager_name', None): + manager = User.objects.filter(name=manager_name, team=self.team).first() if not manager: - raise ValidationError(f'管理员[{manager_username}]不存在') + raise ValidationError(f'管理员[{manager_name}]不存在') attrs['manager'] = manager - else: - attrs['manager'] = None return super().validate(attrs) @@ -87,7 +84,7 @@ class ClientCategorySerializer(BaseSerializer): class ClientCategoryExportSerializer(BaseSerializer): - name = CharField(label='客户分类名称') + name = CharField(label='名称') remark = CharField(label='备注') class Meta: @@ -96,7 +93,7 @@ class ClientCategoryExportSerializer(BaseSerializer): class ClientCategoryImportSerializer(BaseSerializer): - name = CharField(label='客户分类名称(必填)') + name = CharField(label='名称(必填)') remark = CharField(required=False, label='备注') class Meta: @@ -139,6 +136,56 @@ class ClientSerializer(BaseSerializer): return super().update(instance, validated_data) +class ClientExportSerializer(BaseSerializer): + number = CharField(label='编号') + name = CharField(label='名称') + level_display = CharField(source='get_level_display', label='等级') + category_name = CharField(label='分类') + contact = CharField(label='联系人') + phone = CharField(label='手机号') + email = CharField(label='邮箱') + address = CharField(label='地址') + remark = CharField(label='备注') + order = IntegerField(label='排序') + is_active = BooleanField(label='激活状态[TRUE/FALSE]') + initial_arrears_amount = AmountField(label='初期欠款金额') + + class Meta: + model = Client + fields = ['number', 'name', 'level_display', 'category_name', 'contact', 'phone', + 'email', 'address', 'remark', 'order', 'is_active', 'initial_arrears_amount'] + + +class ClientImportSerializer(BaseSerializer): + number = CharField(label='编号(必填)') + name = CharField(label='名称(必填)') + level = CharField(required=False, label='等级[0/1/2/3](默认: 0)') + category_name = CharField(required=False, label='分类') + contact = CharField(required=False, label='联系人') + phone = CharField(required=False, label='手机号') + email = CharField(required=False, label='邮箱') + address = CharField(required=False, label='地址') + remark = CharField(required=False, label='备注') + order = IntegerField(required=False, label='排序(默认: 100)') + is_active = BooleanField(required=False, label='激活状态[TRUE/FALSE](默认: TRUE)') + initial_arrears_amount = AmountField(required=False, label='初期欠款金额(默认: 0)') + + class Meta: + model = Client + fields = ['number', 'name', 'level', 'category_name', 'contact', 'phone', + 'email', 'address', 'remark', 'order', 'is_active', 'initial_arrears_amount'] + + def validate(self, attrs): + if category_name := attrs.pop('category_name', None): + client_category = ClientCategory.objects.filter(name=category_name, team=self.team).first() + if not client_category: + raise ValidationError(f'客户分类[{category_name}]不存在') + + attrs['category'] = client_category + + return super().validate(attrs) + + class SupplierCategorySerializer(BaseSerializer): class Meta: @@ -152,7 +199,7 @@ class SupplierCategorySerializer(BaseSerializer): class SupplierCategoryExportSerializer(BaseSerializer): - name = CharField(label='供应商分类名称') + name = CharField(label='名称') remark = CharField(label='备注') class Meta: @@ -161,7 +208,7 @@ class SupplierCategoryExportSerializer(BaseSerializer): class SupplierCategoryImportSerializer(BaseSerializer): - name = CharField(label='供应商分类名称(必填)') + name = CharField(label='名称(必填)') remark = CharField(required=False, label='备注') class Meta: @@ -203,6 +250,58 @@ class SupplierSerializer(BaseSerializer): return super().update(instance, validated_data) +class SupplierExportSerializer(BaseSerializer): + number = CharField(label='编号') + name = CharField(label='名称') + category_name = CharField(label='分类') + contact = CharField(label='联系人') + phone = CharField(label='手机号') + email = CharField(label='邮箱') + address = CharField(label='地址') + bank_account = CharField(label='银行账户') + bank_name = CharField(label='开户行') + remark = CharField(label='备注') + order = IntegerField(label='排序') + is_active = BooleanField(label='激活状态[TRUE/FALSE]') + initial_arrears_amount = AmountField(label='初期欠款金额') + + class Meta: + model = Supplier + fields = ['number', 'name', 'category_name', 'contact', 'phone', 'email', 'address', + 'bank_account', 'bank_name', 'remark', 'order', 'is_active', 'initial_arrears_amount'] + + +class SupplierImportSerializer(BaseSerializer): + number = CharField(label='编号(必填)') + name = CharField(label='名称(必填)') + category_name = CharField(required=False, label='分类') + contact = CharField(required=False, label='联系人') + phone = CharField(required=False, label='手机号') + email = CharField(required=False, label='邮箱') + address = CharField(required=False, label='地址') + bank_account = CharField(required=False, label='银行账户') + bank_name = CharField(required=False, label='开户行') + remark = CharField(required=False, label='备注') + order = IntegerField(required=False, label='排序(默认: 100)') + is_active = BooleanField(required=False, label='激活状态[TRUE/FALSE](默认: TRUE)') + initial_arrears_amount = AmountField(required=False, label='初期欠款金额(默认: 0)') + + class Meta: + model = Supplier + fields = ['number', 'name', 'category_name', 'contact', 'phone', 'email', 'address', + 'bank_account', 'bank_name', 'remark', 'order', 'is_active', 'initial_arrears_amount'] + + def validate(self, attrs): + if category_name := attrs.pop('category_name', None): + supplier_category = SupplierCategory.objects.filter(name=category_name, team=self.team).first() + if not supplier_category: + raise ValidationError(f'供应商分类[{category_name}]不存在') + + attrs['category'] = supplier_category + + return super().validate(attrs) + + class AccountSerializer(BaseSerializer): type_display = CharField(source='get_type_display', read_only=True, label='账户类型') @@ -233,6 +332,38 @@ class AccountSerializer(BaseSerializer): return super().update(instance, validated_data) +class AccountExportSerializer(BaseSerializer): + number = CharField(label='编号') + name = CharField(label='名称') + type_display = CharField(source='get_type_display', label='账户类型') + holder = CharField(label='开户人') + remark = CharField(label='备注') + order = IntegerField(label='排序') + is_active = BooleanField(label='激活状态[TRUE/FALSE]') + initial_balance_amount = AmountField(label='初期余额') + + class Meta: + model = Account + fields = ['number', 'name', 'type_display', 'holder', 'remark', 'order', 'is_active', + 'initial_balance_amount'] + + +class AccountImportSerializer(BaseSerializer): + number = CharField(label='编号(必填)') + name = CharField(label='名称(必填)') + type = CharField(required=False, label='账户类型[cash/alipay/wechat/bank_account/other](默认: cash)') + holder = CharField(required=False, label='开户人') + remark = CharField(required=False, label='备注') + order = IntegerField(required=False, label='排序(默认: 100)') + is_active = BooleanField(required=False, label='激活状态[TRUE/FALSE](默认: TRUE)') + initial_balance_amount = AmountField(required=False, label='初期余额(默认: 0)') + + class Meta: + model = Account + fields = ['number', 'name', 'type', 'holder', 'remark', 'order', 'is_active', + 'initial_balance_amount'] + + class ChargeItemSerializer(BaseSerializer): type_display = CharField(source='get_type_display', read_only=True, label='收支类型') @@ -267,11 +398,11 @@ class ChargeItemImportSerializer(BaseSerializer): __all__ = [ - 'WarehouseSerializer', + 'WarehouseSerializer', 'WarehouseExportSerializer', 'WarehouseImportSerializer', 'ClientCategorySerializer', 'ClientCategoryExportSerializer', 'ClientCategoryImportSerializer', - 'ClientSerializer', + 'ClientSerializer', 'ClientExportSerializer', 'ClientImportSerializer', 'SupplierCategorySerializer', 'SupplierCategoryExportSerializer', 'SupplierCategoryImportSerializer', - 'SupplierSerializer', - 'AccountSerializer', + 'SupplierSerializer', 'SupplierExportSerializer', 'SupplierImportSerializer', + 'AccountSerializer', 'AccountExportSerializer', 'AccountImportSerializer', 'ChargeItemSerializer', 'ChargeItemExportSerializer', 'ChargeItemImportSerializer', ] diff --git a/apps/data/views.py b/apps/data/views.py index dcc92d9..38ab0c1 100644 --- a/apps/data/views.py +++ b/apps/data/views.py @@ -63,6 +63,47 @@ class WarehouseViewSet(ModelViewSet, DataProtectMixin, ExportMixin, ImportMixin) serializer = WarehouseSerializer(instance=warehouse) return Response(data=serializer.data, status=status.HTTP_200_OK) + @extend_schema(responses={200: DownloadResponse}) + @action(detail=False, methods=['get']) + def export(self, request, *args, **kwargs): + """导出""" + + return self.get_export_response(WarehouseExportSerializer) + + @extend_schema(responses={200: DownloadResponse}) + @action(detail=False, methods=['get']) + def import_template(self, request, *args, **kwargs): + """导入模板""" + + return self.get_template_response(WarehouseImportSerializer) + + @extend_schema(request=UploadRequest, responses={200: WarehouseSerializer(many=True)}) + @action(detail=False, methods=['post']) + @transaction.atomic + def import_data(self, request, *args, **kwargs): + """导入数据""" + + request_serializer = UploadRequest(data=request.data) + request_serializer.is_valid(raise_exception=True) + validated_data = request_serializer.validated_data + + warehouses = [] + for import_serializer in self.load_data(validated_data['file'], WarehouseImportSerializer): + validated_data = import_serializer.validated_data + if warehouse := Warehouse.objects.filter(name=validated_data['name'], + team=self.team).first(): + serializer = WarehouseSerializer(instance=warehouse, data=validated_data, + context=self.context) + else: + serializer = WarehouseSerializer(data=validated_data, context=self.context) + + serializer.is_valid(raise_exception=True) + warehouse = serializer.save() + warehouses.append(warehouse) + + serializer = WarehouseSerializer(instance=warehouses, many=True) + return Response(data=serializer.data, status=status.HTTP_200_OK) + class ClientCategoryViewSet(ModelViewSet, ExportMixin, ImportMixin): """客户分类""" @@ -135,6 +176,47 @@ class ClientViewSet(ModelViewSet, DataProtectMixin, ExportMixin, ImportMixin): number = Client.get_number(self.team) return Response(data={'number': number}, status=status.HTTP_200_OK) + @extend_schema(responses={200: DownloadResponse}) + @action(detail=False, methods=['get']) + def export(self, request, *args, **kwargs): + """导出""" + + return self.get_export_response(ClientExportSerializer) + + @extend_schema(responses={200: DownloadResponse}) + @action(detail=False, methods=['get']) + def import_template(self, request, *args, **kwargs): + """导入模板""" + + return self.get_template_response(ClientImportSerializer) + + @extend_schema(request=UploadRequest, responses={200: ClientSerializer(many=True)}) + @action(detail=False, methods=['post']) + @transaction.atomic + def import_data(self, request, *args, **kwargs): + """导入数据""" + + request_serializer = UploadRequest(data=request.data) + request_serializer.is_valid(raise_exception=True) + validated_data = request_serializer.validated_data + + clients = [] + for import_serializer in self.load_data(validated_data['file'], ClientImportSerializer): + validated_data = import_serializer.validated_data + if client := Client.objects.filter(name=validated_data['name'], + team=self.team).first(): + serializer = ClientSerializer(instance=client, data=validated_data, + context=self.context) + else: + serializer = ClientSerializer(data=validated_data, context=self.context) + + serializer.is_valid(raise_exception=True) + client = serializer.save() + clients.append(client) + + serializer = ClientSerializer(instance=clients, many=True) + return Response(data=serializer.data, status=status.HTTP_200_OK) + class SupplierCategoryViewSet(ModelViewSet, ExportMixin, ImportMixin): """供应商分类""" @@ -207,6 +289,47 @@ class SupplierViewSet(ModelViewSet, DataProtectMixin, ExportMixin, ImportMixin): number = Supplier.get_number(self.team) return Response(data={'number': number}, status=status.HTTP_200_OK) + @extend_schema(responses={200: DownloadResponse}) + @action(detail=False, methods=['get']) + def export(self, request, *args, **kwargs): + """导出""" + + return self.get_export_response(SupplierExportSerializer) + + @extend_schema(responses={200: DownloadResponse}) + @action(detail=False, methods=['get']) + def import_template(self, request, *args, **kwargs): + """导入模板""" + + return self.get_template_response(SupplierImportSerializer) + + @extend_schema(request=UploadRequest, responses={200: SupplierSerializer(many=True)}) + @action(detail=False, methods=['post']) + @transaction.atomic + def import_data(self, request, *args, **kwargs): + """导入数据""" + + request_serializer = UploadRequest(data=request.data) + request_serializer.is_valid(raise_exception=True) + validated_data = request_serializer.validated_data + + suppliers = [] + for import_serializer in self.load_data(validated_data['file'], SupplierImportSerializer): + validated_data = import_serializer.validated_data + if supplier := Supplier.objects.filter(name=validated_data['name'], + team=self.team).first(): + serializer = SupplierSerializer(instance=supplier, data=validated_data, + context=self.context) + else: + serializer = SupplierSerializer(data=validated_data, context=self.context) + + serializer.is_valid(raise_exception=True) + supplier = serializer.save() + suppliers.append(supplier) + + serializer = SupplierSerializer(instance=suppliers, many=True) + return Response(data=serializer.data, status=status.HTTP_200_OK) + class AccountViewSet(ModelViewSet, DataProtectMixin, ExportMixin, ImportMixin): """结算账户""" @@ -227,6 +350,47 @@ class AccountViewSet(ModelViewSet, DataProtectMixin, ExportMixin, ImportMixin): number = Account.get_number(self.team) return Response(data={'number': number}, status=status.HTTP_200_OK) + @extend_schema(responses={200: DownloadResponse}) + @action(detail=False, methods=['get']) + def export(self, request, *args, **kwargs): + """导出""" + + return self.get_export_response(AccountExportSerializer) + + @extend_schema(responses={200: DownloadResponse}) + @action(detail=False, methods=['get']) + def import_template(self, request, *args, **kwargs): + """导入模板""" + + return self.get_template_response(AccountImportSerializer) + + @extend_schema(request=UploadRequest, responses={200: AccountSerializer(many=True)}) + @action(detail=False, methods=['post']) + @transaction.atomic + def import_data(self, request, *args, **kwargs): + """导入数据""" + + request_serializer = UploadRequest(data=request.data) + request_serializer.is_valid(raise_exception=True) + validated_data = request_serializer.validated_data + + accounts = [] + for import_serializer in self.load_data(validated_data['file'], AccountImportSerializer): + validated_data = import_serializer.validated_data + if account := Account.objects.filter(name=validated_data['name'], + team=self.team).first(): + serializer = AccountSerializer(instance=account, data=validated_data, + context=self.context) + else: + serializer = AccountSerializer(data=validated_data, context=self.context) + + serializer.is_valid(raise_exception=True) + account = serializer.save() + accounts.append(account) + + serializer = AccountSerializer(instance=accounts, many=True) + return Response(data=serializer.data, status=status.HTTP_200_OK) + class ChargeItemViewSet(ModelViewSet, ExportMixin, ImportMixin): """收支项目""" diff --git a/apps/goods/models.py b/apps/goods/models.py index 178ec28..1b12673 100644 --- a/apps/goods/models.py +++ b/apps/goods/models.py @@ -34,7 +34,7 @@ class Goods(Model): related_name='goods_set', verbose_name='商品分类') unit = ForeignKey('goods.GoodsUnit', on_delete=SET_NULL, null=True, related_name='goods_set', verbose_name='商品单位') - spec = CharField(max_length=64, null=True, blank=True, verbose_name='商品规格') + spec = CharField(max_length=64, null=True, blank=True, verbose_name='规格') enable_batch_control = BooleanField(default=False, verbose_name='启用批次控制') shelf_life_days = IntegerField(null=True, verbose_name='保质期天数') shelf_life_warning_days = IntegerField(default=0, verbose_name='保质期预警天数') diff --git a/apps/goods/serializers.py b/apps/goods/serializers.py index 71f9c80..676e953 100644 --- a/apps/goods/serializers.py +++ b/apps/goods/serializers.py @@ -108,10 +108,10 @@ class GoodsSerializer(BaseSerializer): category_name = CharField(source='category.name', read_only=True, label='分类名称') unit_name = CharField(source='unit.name', read_only=True, label='单位名称') - inventory_items = InventoryItemSerializer(source='inventories', required=False, - many=True, label='库存Item') - goods_image_items = GoodsImageItemSerializer(source='goods_images', many=True, - read_only=True, label='商品图片Item') + inventory_items = InventoryItemSerializer( + source='inventories', required=False, many=True, label='库存Item') + goods_image_items = GoodsImageItemSerializer( + source='goods_images', many=True, read_only=True, label='商品图片Item') class Meta: model = Goods @@ -295,6 +295,85 @@ class GoodsSerializer(BaseSerializer): return goods +class GoodsExportSerializer(BaseSerializer): + number = CharField(label='编号') + name = CharField(label='名称') + barcode = CharField(label='条码') + category_name = CharField(label='分类') + unit_name = CharField(label='单位') + spec = CharField(label='规格') + enable_batch_control = BooleanField(label='启用批次控制[TRUE/FALSE]') + shelf_life_days = IntegerField(label='保质期天数') + shelf_life_warning_days = IntegerField(label='保质期预警天数') + enable_inventory_warning = BooleanField(label='启用库存警告[TRUE/FALSE]') + inventory_upper = FloatField(label='库存上限') + inventory_lower = FloatField(label='库存下限') + purchase_price = FloatField(label='采购价') + retail_price = FloatField(label='零售价') + level_price1 = FloatField(label='等级价一') + level_price2 = FloatField(label='等级价二') + level_price3 = FloatField(label='等级价三') + remark = CharField(label='备注') + order = IntegerField(label='排序') + is_active = BooleanField(label='激活状态[TRUE/FALSE]') + + class Meta: + model = Goods + fields = ['number', 'name', 'barcode', 'category_name', 'unit_name', 'spec', + 'enable_batch_control', 'shelf_life_days', 'shelf_life_warning_days', + 'enable_inventory_warning', 'inventory_upper', 'inventory_lower', + 'purchase_price', 'retail_price', 'level_price1', 'level_price2', + 'level_price3', 'remark', 'order', 'is_active'] + + +class GoodsImportSerializer(BaseSerializer): + number = CharField(label='编号') + name = CharField(label='名称') + barcode = CharField(required=False, label='条码') + category_name = CharField(required=False, label='分类') + unit_name = CharField(required=False, label='单位') + spec = CharField(required=False, label='规格') + enable_batch_control = BooleanField(required=False, label='启用批次控制[TRUE/FALSE](默认: FALSE)') + shelf_life_days = IntegerField(required=False, label='保质期天数') + shelf_life_warning_days = IntegerField(required=False, label='保质期预警天数') + enable_inventory_warning = BooleanField(required=False, label='启用库存警告[TRUE/FALSE](默认: FALSE)') + inventory_upper = FloatField(required=False, label='库存上限') + inventory_lower = FloatField(required=False, label='库存下限') + purchase_price = FloatField(label='采购价') + retail_price = FloatField(label='零售价') + level_price1 = FloatField(label='等级价一') + level_price2 = FloatField(label='等级价二') + level_price3 = FloatField(label='等级价三') + remark = CharField(required=False, label='备注') + order = IntegerField(required=False, label='排序(默认: 100)') + is_active = BooleanField(required=False, label='激活状态[TRUE/FALSE](默认: TRUE)') + + class Meta: + model = Goods + fields = ['number', 'name', 'barcode', 'category_name', 'unit_name', 'spec', + 'enable_batch_control', 'shelf_life_days', 'shelf_life_warning_days', + 'enable_inventory_warning', 'inventory_upper', 'inventory_lower', + 'purchase_price', 'retail_price', 'level_price1', 'level_price2', + 'level_price3', 'remark', 'order', 'is_active'] + + def validate(self, attrs): + if category_name := attrs.pop('category_name', None): + goods_category = GoodsCategory.objects.filter(name=category_name, team=self.team).first() + if not goods_category: + raise ValidationError(f'商品分类[{category_name}]不存在') + + attrs['category'] = goods_category + + if unit_name := attrs.pop('unit_name', None): + goods_unit = GoodsUnit.objects.filter(name=unit_name, team=self.team).first() + if not goods_unit: + raise ValidationError(f'商品分类[{unit_name}]不存在') + + attrs['unit'] = goods_unit + + return super().validate(attrs) + + class GoodsImageSerializer(BaseSerializer): class Meta: @@ -339,7 +418,7 @@ class InventorySerializer(BaseSerializer): __all__ = [ 'GoodsCategorySerializer', 'GoodsCategoryExportSerializer', 'GoodsCategoryImportSerializer', 'GoodsUnitSerializer', 'GoodsUnitExportSerializer', 'GoodsUnitImportSerializer', - 'GoodsSerializer', + 'GoodsSerializer', 'GoodsExportSerializer', 'GoodsImportSerializer', 'GoodsImageSerializer', 'BatchSerializer', 'InventorySerializer', ] diff --git a/apps/goods/views.py b/apps/goods/views.py index 403db3f..3c53cc9 100644 --- a/apps/goods/views.py +++ b/apps/goods/views.py @@ -134,6 +134,47 @@ class GoodsViewSet(ModelViewSet, DataProtectMixin, ExportMixin, ImportMixin): number = Goods.get_number(self.team) return Response(data={'number': number}, status=status.HTTP_200_OK) + @extend_schema(responses={200: DownloadResponse}) + @action(detail=False, methods=['get']) + def export(self, request, *args, **kwargs): + """导出""" + + return self.get_export_response(GoodsExportSerializer) + + @extend_schema(responses={200: DownloadResponse}) + @action(detail=False, methods=['get']) + def import_template(self, request, *args, **kwargs): + """导入模板""" + + return self.get_template_response(GoodsImportSerializer) + + @extend_schema(request=UploadRequest, responses={200: GoodsSerializer(many=True)}) + @action(detail=False, methods=['post']) + @transaction.atomic + def import_data(self, request, *args, **kwargs): + """导入数据""" + + request_serializer = UploadRequest(data=request.data) + request_serializer.is_valid(raise_exception=True) + validated_data = request_serializer.validated_data + + goods_set = [] + for import_serializer in self.load_data(validated_data['file'], GoodsImportSerializer): + validated_data = import_serializer.validated_data + if goods := Goods.objects.filter(name=validated_data['name'], + team=self.team).first(): + serializer = GoodsSerializer(instance=goods, data=validated_data, + context=self.context) + else: + serializer = GoodsSerializer(data=validated_data, context=self.context) + + serializer.is_valid(raise_exception=True) + goods = serializer.save() + goods_set.append(goods) + + serializer = GoodsSerializer(instance=goods_set, many=True) + return Response(data=serializer.data, status=status.HTTP_200_OK) + class GoodsImageViewSet(ModelViewSet): """商品图片""" diff --git a/apps/system/models.py b/apps/system/models.py index 21542ae..be7e3d1 100644 --- a/apps/system/models.py +++ b/apps/system/models.py @@ -61,7 +61,7 @@ class User(Model): team = ForeignKey('system.Team', on_delete=CASCADE, related_name='users') class Meta: - unique_together = [('username', 'team')] + unique_together = [('username', 'team'), ('name', 'team')] __all__ = [ diff --git a/apps/system/serializers.py b/apps/system/serializers.py index 6b97cd5..e97a843 100644 --- a/apps/system/serializers.py +++ b/apps/system/serializers.py @@ -34,6 +34,10 @@ class RoleSerializer(BaseSerializer): read_only_fields = ['id'] fields = ['name', 'remark', 'permissions', *read_only_fields] + def validate_name(self, value): + self.validate_unique({'name': value}, message=f'名称[{value}]已存在') + return value + class UserSerializer(BaseSerializer): @@ -55,6 +59,10 @@ class UserSerializer(BaseSerializer): self.validate_unique({'username': value}, message=f'用户名[{value}]已存在') return value + def validate_name(self, value): + self.validate_unique({'name': value}, message=f'名称[{value}]已存在') + return value + def validate_roles(self, instances): instances = self.validate_foreign_key_set(Role, instances, message='角色不存在') return instances