forked from erp-dev/erp
feat: query api for stock_change_detail
This commit is contained in:
@@ -219,3 +219,142 @@ class ListStockChangesView(StockChangeViewMixin, views.APIView):
|
||||
'error': '查询库存变动记录列表失败',
|
||||
'message': str(e)
|
||||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
|
||||
class ListStockChangeDetailsView(StockChangeViewMixin, views.APIView):
|
||||
"""获取库存变动明细列表"""
|
||||
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
@extend_schema(
|
||||
tags=['读取库存明细'],
|
||||
parameters=[
|
||||
OpenApiParameter(name='warehouse_id', type=OpenApiTypes.INT, required=True, description='仓库ID'),
|
||||
OpenApiParameter(name='product_id', type=OpenApiTypes.INT, required=True, description='产品ID'),
|
||||
OpenApiParameter(name='direction', type=OpenApiTypes.STR, required=False, description='变动方向 (inbound=入库, outbound=出库),默认为inbound'),
|
||||
],
|
||||
responses={200: dict},
|
||||
summary="获取库存变动明细列表",
|
||||
description="根据仓库和产品查询库存变动明细,不查询StockChangeRecord"
|
||||
)
|
||||
def get(self, request):
|
||||
"""
|
||||
获取库存变动明细列表(根据仓库和产品查询)
|
||||
|
||||
查询参数:
|
||||
- warehouse_id: 仓库ID,必填
|
||||
- product_id: 产品ID,必填
|
||||
- direction: 变动方向 (inbound=入库, outbound=出库),可选,默认为inbound
|
||||
"""
|
||||
|
||||
# 检查员工权限
|
||||
if not self.check_employee_permission(request):
|
||||
return self.permission_error_response('无权限访问')
|
||||
|
||||
merchant_id = self.get_merchant_id(request)
|
||||
|
||||
try:
|
||||
# 获取查询参数
|
||||
warehouse_id_str = request.GET.get('warehouse_id')
|
||||
product_id_str = request.GET.get('product_id')
|
||||
direction = request.GET.get('direction', 'inbound') # 默认为入库
|
||||
|
||||
# 验证必填参数
|
||||
if not warehouse_id_str:
|
||||
return Response({
|
||||
'error': '缺少必要参数',
|
||||
'message': 'warehouse_id 参数为必填'
|
||||
}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
if not product_id_str:
|
||||
return Response({
|
||||
'error': '缺少必要参数',
|
||||
'message': 'product_id 参数为必填'
|
||||
}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
# 验证direction参数
|
||||
if direction not in ['inbound', 'outbound']:
|
||||
return Response({
|
||||
'error': '参数格式错误',
|
||||
'message': 'direction 参数必须为 inbound 或 outbound'
|
||||
}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
# 解析参数
|
||||
try:
|
||||
warehouse_id = int(warehouse_id_str)
|
||||
product_id = int(product_id_str)
|
||||
except ValueError:
|
||||
return Response({
|
||||
'error': '参数格式错误',
|
||||
'message': 'warehouse_id 和 product_id 必须为整数'
|
||||
}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
# 验证仓库是否对用户可见
|
||||
if not self.validate_warehouse_visibility(warehouse_id, request):
|
||||
return Response({
|
||||
'error': f'仓库ID {warehouse_id} 对当前用户不可见'
|
||||
}, status=status.HTTP_403_FORBIDDEN)
|
||||
|
||||
# 验证产品是否对用户可见
|
||||
if not self.validate_product_visibility(product_id, request):
|
||||
return Response({
|
||||
'error': f'产品ID {product_id} 对当前用户不可见'
|
||||
}, status=status.HTTP_403_FORBIDDEN)
|
||||
|
||||
# 根据direction参数确定变动类型
|
||||
if direction == 'inbound':
|
||||
change_type = stock_models.StockChangeTypeEnum.ADD # 入库
|
||||
else: # outbound
|
||||
change_type = stock_models.StockChangeTypeEnum.REMOVE # 出库
|
||||
|
||||
# 查询库存变动明细,仅查询未被消耗的明细(consumed_by_detail为空)
|
||||
details = stock_models.StockChangeDetail.objects.filter(
|
||||
stock_change_record__merchant_id=merchant_id,
|
||||
stock_change_record__warehouse_id=warehouse_id,
|
||||
stock_change_record__type=change_type,
|
||||
product_id=product_id,
|
||||
consumed_by_detail__isnull=True # 仅查询未被消耗的明细
|
||||
).select_related(
|
||||
'stock_change_record',
|
||||
'product'
|
||||
).order_by('-stock_change_record__created_at')
|
||||
|
||||
# 构建响应数据
|
||||
results = []
|
||||
for detail in details:
|
||||
detail_data = self.build_detail_data(detail)
|
||||
# 添加记录的额外信息
|
||||
detail_data['record_type'] = detail.stock_change_record.type
|
||||
detail_data['record_type_display'] = detail.stock_change_record.get_type_display()
|
||||
detail_data['source_type'] = detail.stock_change_record.source_type
|
||||
detail_data['source_type_display'] = detail.stock_change_record.get_source_type_display()
|
||||
detail_data['record_created_at'] = detail.stock_change_record.created_at
|
||||
detail_data['record_created_by'] = detail.stock_change_record.created_by.username
|
||||
results.append(detail_data)
|
||||
|
||||
response_data = {
|
||||
'count': len(results),
|
||||
'results': results,
|
||||
'filters': {
|
||||
'warehouse_id': warehouse_id,
|
||||
'product_id': product_id,
|
||||
'direction': direction
|
||||
}
|
||||
}
|
||||
|
||||
logger.info(
|
||||
f"用户 {request.user.username} 查询库存变动明细,"
|
||||
f"仓库ID: {warehouse_id},产品ID: {product_id},共 {len(results)} 条"
|
||||
)
|
||||
return Response(response_data, status=status.HTTP_200_OK)
|
||||
|
||||
except AttributeError as e:
|
||||
logger.error(f"用户 {request.user.username} 无员工信息: {str(e)}", exc_info=True)
|
||||
return self.permission_error_response('用户无员工信息,无权访问')
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"查询库存变动明细失败: {str(e)}", exc_info=True)
|
||||
return Response({
|
||||
'error': '查询库存变动明细失败',
|
||||
'message': str(e)
|
||||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
Reference in New Issue
Block a user