1
0
forked from erp-dev/erp
Files
erpnew/api_v1/views/inventory.py

177 lines
6.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from rest_framework.generics import GenericAPIView
from rest_framework.mixins import ListModelMixin
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from rest_framework import serializers, status
from rest_framework.pagination import LimitOffsetPagination
from stock import models as stock_models
from basic_info import models as basic_models
from api_man.serializers import ProductSerializer
import logging
logger = logging.getLogger(__name__)
class WarehouseSimpleSerializer(serializers.ModelSerializer):
"""仓库简单序列化器"""
class Meta:
model = basic_models.WareHouse
fields = ['id', 'name', 'location', 'area']
class InventorySerializer(serializers.ModelSerializer):
"""库存项序列化器"""
product = ProductSerializer(read_only=True)
warehouse = WarehouseSimpleSerializer(read_only=True)
class Meta:
model = stock_models.Inventory
fields = [
'id',
'product',
'warehouse',
'quantity',
'num_of_rolls',
'spec',
]
def __init__(self, *args, **kwargs):
"""重写初始化方法,确保 context 传递给嵌套序列化器"""
super().__init__(*args, **kwargs)
# 如果有 context将其传递给嵌套的序列化器字段
if hasattr(self, 'context'):
for field_name, field in self.fields.items():
if isinstance(field, serializers.ModelSerializer):
field.context.update(self.context)
class InventoryAPIView(ListModelMixin, GenericAPIView):
"""
库存查询接口
只返回当前用户所属商户的库存信息
查询参数:
- limit: 返回的记录数量默认20最大100
- offset: 跳过的记录数量默认0
- warehouse: 仓库ID可选
- product: 产品ID可选
示例:
- /api/v1/inventory/ - 获取前20条
- /api/v1/inventory/?limit=50&offset=0 - 获取前50条
- /api/v1/inventory/?limit=20&offset=40 - 跳过前40条获取接下来的20条
"""
serializer_class = InventorySerializer
pagination_class = LimitOffsetPagination
permission_classes = [IsAuthenticated]
def get_queryset(self):
"""
获取查询集,自动过滤当前用户所属商户的库存
"""
# 检查用户是否有员工身份
if not hasattr(self.request.user, 'employee'):
logger.warning(f"用户 {self.request.user.username} 无员工信息,拒绝访问库存")
return stock_models.Inventory.objects.none()
employee = self.request.user.employee
merchant_id = employee.merchant_id
# 使用 select_related 优化查询,只查询当前商户的库存
queryset = stock_models.Inventory.objects.filter(
merchant_id=merchant_id
).select_related(
'product',
'product__category',
'warehouse'
).order_by('-quantity', 'product__name')
# 可选过滤:按仓库
warehouse_id = self.request.query_params.get('warehouse')
if warehouse_id:
try:
warehouse_id = int(warehouse_id)
# 验证仓库属于当前商户
if basic_models.WareHouse.objects.filter(
id=warehouse_id,
merchant_id=merchant_id
).exists():
queryset = queryset.filter(warehouse_id=warehouse_id)
else:
logger.warning(f"仓库ID {warehouse_id} 不属于商户 {merchant_id}")
return queryset.none()
except ValueError:
logger.warning(f"warehouse 参数格式错误: {warehouse_id}")
return queryset.none()
# 可选过滤:按产品
product_id = self.request.query_params.get('product')
if product_id:
try:
product_id = int(product_id)
# 验证产品属于当前商户
if basic_models.Product.objects.filter(
id=product_id,
merchant_id=merchant_id
).exists():
queryset = queryset.filter(product_id=product_id)
else:
logger.warning(f"产品ID {product_id} 不属于商户 {merchant_id}")
return queryset.none()
except ValueError:
logger.warning(f"product 参数格式错误: {product_id}")
return queryset.none()
return queryset
def get_serializer_context(self):
"""
确保序列化器获得 request context
"""
context = super().get_serializer_context()
context['request'] = self.request
return context
def list(self, request, *args, **kwargs):
"""
重写 list 方法,添加统计信息
"""
queryset = self.filter_queryset(self.get_queryset())
# 统计信息(在分页前)
total_items = queryset.count()
total_quantity = sum(item.quantity for item in queryset)
total_rolls = sum(item.num_of_rolls for item in queryset)
# 使用 ListModelMixin 的分页和序列化逻辑
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
response = self.get_paginated_response(serializer.data)
# 添加统计信息
response.data['statistics'] = {
'total_items': total_items,
'total_quantity': float(total_quantity),
'total_rolls': total_rolls,
}
logger.info(
f"用户 {request.user.username} 查询库存列表,"
f"商户ID: {request.user.employee.merchant_id},共 {total_items} 条记录"
)
return response
# 如果没有分页,返回全部数据
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
def get(self, request, *args, **kwargs):
"""
GET 请求处理
"""
return self.list(request, *args, **kwargs)