mirror of
https://github.com/himool/HimoolERP.git
synced 2024-11-15 03:47:42 +08:00
feat: 基础数据导入导出
This commit is contained in:
parent
c551d09e52
commit
42bb5af691
8 changed files with 454 additions and 31 deletions
|
@ -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='排序')
|
||||
|
|
|
@ -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',
|
||||
]
|
||||
|
|
|
@ -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):
|
||||
"""收支项目"""
|
||||
|
|
|
@ -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='保质期预警天数')
|
||||
|
|
|
@ -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',
|
||||
]
|
||||
|
|
|
@ -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):
|
||||
"""商品图片"""
|
||||
|
|
|
@ -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__ = [
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue