forked from erp-dev/erp
300 lines
12 KiB
Python
300 lines
12 KiB
Python
"""
|
||
创建库存变动记录视图
|
||
"""
|
||
|
||
from rest_framework import status, views
|
||
from rest_framework.response import Response
|
||
from rest_framework.permissions import IsAuthenticated
|
||
import logging
|
||
|
||
from stock import services as stock_services
|
||
from drf_spectacular.utils import extend_schema
|
||
from api_v1 import serializers
|
||
|
||
from .mixins import StockChangeViewMixin
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
class CreateStockChangeView(StockChangeViewMixin, views.APIView):
|
||
"""创建完整的库存变动记录"""
|
||
|
||
permission_classes = [IsAuthenticated]
|
||
|
||
@extend_schema(
|
||
tags=['创建出入库'],
|
||
request=serializers.CreateStockChangeSerializer,
|
||
responses={201: serializers.CreateStockChangeResponseSerializer},
|
||
summary="创建完整的库存变动记录",
|
||
description="创建一个新的库存变动记录及其明细"
|
||
)
|
||
def post(self, request):
|
||
"""
|
||
创建完整的库存变动记录(包含记录创建参数)
|
||
|
||
请求参数:
|
||
{
|
||
"type": 1, // 1=入库, 2=出库
|
||
"warehouse": 1, // 仓库ID
|
||
"source_type": 1, // 来源类型
|
||
"source_id": 1, // 可选,来源单据ID
|
||
"products": [
|
||
{
|
||
"product": 1,
|
||
"quantity": [85, 75, 90]
|
||
}
|
||
]
|
||
}
|
||
"""
|
||
|
||
# 检查员工权限
|
||
if not self.check_employee_permission(request):
|
||
return self.permission_error_response('无权限访问')
|
||
|
||
# 提取库存变动记录参数
|
||
record_data = {
|
||
'type': request.data.get('type'),
|
||
'warehouse': request.data.get('warehouse'),
|
||
'source_type': request.data.get('source_type'),
|
||
'source_id': request.data.get('source_id'),
|
||
}
|
||
|
||
# 验证必要参数
|
||
if not all([record_data['type'], record_data['warehouse'], record_data['source_type']]):
|
||
return Response({
|
||
'error': '缺少必要参数',
|
||
'message': '请提供 type, warehouse, source_type'
|
||
}, status=status.HTTP_400_BAD_REQUEST)
|
||
|
||
products_data = request.data.get('products', [])
|
||
if not products_data:
|
||
return Response({
|
||
'error': '产品列表不能为空'
|
||
}, status=status.HTTP_400_BAD_REQUEST)
|
||
|
||
# 验证仓库是否本员工可见
|
||
if not self.validate_warehouse_visibility(record_data['warehouse'], request):
|
||
return Response({
|
||
'error': f'仓库ID {record_data["warehouse"]} 对当前用户不可见'
|
||
}, status=status.HTTP_403_FORBIDDEN)
|
||
|
||
# 验证产品是否本员工可见
|
||
for p in products_data:
|
||
product_id = p.get('product')
|
||
if not self.validate_product_visibility(product_id, request):
|
||
return Response({
|
||
'error': f'产品ID {product_id} 对当前用户不可见'
|
||
}, status=status.HTTP_403_FORBIDDEN)
|
||
|
||
# 验证产品数据
|
||
product_serializer = serializers.CreateStockChangeSerializer(data={
|
||
'products': products_data
|
||
})
|
||
if not product_serializer.is_valid():
|
||
return Response({
|
||
'error': '产品数据验证失败',
|
||
'details': product_serializer.errors
|
||
}, status=status.HTTP_400_BAD_REQUEST)
|
||
|
||
try:
|
||
stock_change_record, created_details, created_count = stock_services.create_stock_change_record_with_details(
|
||
merchant=request.user.employee.merchant,
|
||
created_by=request.user,
|
||
type=record_data['type'],
|
||
warehouse_id=record_data['warehouse'],
|
||
source_type=record_data['source_type'],
|
||
source_id=record_data['source_id'],
|
||
products=products_data,
|
||
)
|
||
response_serializer = serializers.CreateStockChangeResponseSerializer({
|
||
'stock_change_record': stock_change_record,
|
||
'details': created_details,
|
||
'message': f'成功创建库存变动记录及 {created_count} 条明细',
|
||
'created_details_count': created_count
|
||
})
|
||
|
||
return Response(response_serializer.data, status=status.HTTP_201_CREATED)
|
||
|
||
except ValueError as e:
|
||
return Response({
|
||
'error': str(e)
|
||
}, status=status.HTTP_400_BAD_REQUEST)
|
||
|
||
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)
|
||
|
||
|
||
class CreateStockChangeRelaxedView(StockChangeViewMixin, views.APIView):
|
||
"""宽松模式:根据总量与单条数量拆分的出入库记录"""
|
||
|
||
permission_classes = [IsAuthenticated]
|
||
|
||
@extend_schema(
|
||
tags=['创建出入库'],
|
||
request=serializers.CreateStockChangeRelaxedSerializer,
|
||
responses={201: serializers.CreateStockChangeResponseSerializer},
|
||
summary="创建库存变动记录(宽松模式)",
|
||
description="根据总数量与单条数量自动拆分明细"
|
||
)
|
||
def post(self, request):
|
||
if not self.check_employee_permission(request):
|
||
return self.permission_error_response('无权限访问')
|
||
|
||
record_data = {
|
||
'type': request.data.get('type'),
|
||
'warehouse': request.data.get('warehouse'),
|
||
'source_type': request.data.get('source_type'),
|
||
'source_id': request.data.get('source_id'),
|
||
}
|
||
|
||
if not all([record_data['type'], record_data['warehouse'], record_data['source_type']]):
|
||
return Response({
|
||
'error': '缺少必要参数',
|
||
'message': '请提供 type, warehouse, source_type'
|
||
}, status=status.HTTP_400_BAD_REQUEST)
|
||
|
||
products_data = request.data.get('products', [])
|
||
if not products_data:
|
||
return Response({
|
||
'error': '产品列表不能为空'
|
||
}, status=status.HTTP_400_BAD_REQUEST)
|
||
|
||
if not self.validate_warehouse_visibility(record_data['warehouse'], request):
|
||
return Response({
|
||
'error': f'仓库ID {record_data["warehouse"]} 对当前用户不可见'
|
||
}, status=status.HTTP_403_FORBIDDEN)
|
||
|
||
for p in products_data:
|
||
product_id = p.get('product')
|
||
if not self.validate_product_visibility(product_id, request):
|
||
return Response({
|
||
'error': f'产品ID {product_id} 对当前用户不可见'
|
||
}, status=status.HTTP_403_FORBIDDEN)
|
||
|
||
serializer = serializers.CreateStockChangeRelaxedSerializer(data={'products': products_data})
|
||
if not serializer.is_valid():
|
||
return Response({
|
||
'error': '产品数据验证失败',
|
||
'details': serializer.errors
|
||
}, status=status.HTTP_400_BAD_REQUEST)
|
||
|
||
try:
|
||
stock_change_record, created_details, created_count = stock_services.create_stock_change_record_relaxed(
|
||
merchant=request.user.employee.merchant,
|
||
created_by=request.user,
|
||
type=record_data['type'],
|
||
warehouse_id=record_data['warehouse'],
|
||
source_type=record_data['source_type'],
|
||
source_id=record_data['source_id'],
|
||
products=products_data,
|
||
)
|
||
response_serializer = serializers.CreateStockChangeResponseSerializer({
|
||
'stock_change_record': stock_change_record,
|
||
'details': created_details,
|
||
'message': f'成功创建库存变动记录及 {created_count} 条明细',
|
||
'created_details_count': created_count
|
||
})
|
||
|
||
return Response(response_serializer.data, status=status.HTTP_201_CREATED)
|
||
|
||
except ValueError as e:
|
||
return Response({
|
||
'error': str(e)
|
||
}, status=status.HTTP_400_BAD_REQUEST)
|
||
|
||
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)
|
||
|
||
|
||
class CreateStockChangeRestrictView(StockChangeViewMixin, views.APIView):
|
||
"""严进严出模式接口"""
|
||
permission_classes = [IsAuthenticated]
|
||
|
||
@extend_schema(
|
||
tags=['创建出入库'],
|
||
request=serializers.CreateStockChangeSerializer,
|
||
responses={201: serializers.CreateStockChangeResponseSerializer},
|
||
summary="创建库存变动记录(严进严出模式)",
|
||
description="严进严出模式会在出库时按 consume_with 消耗入库明细"
|
||
)
|
||
def post(self, request):
|
||
if not self.check_employee_permission(request):
|
||
return self.permission_error_response('无权限访问')
|
||
|
||
record_data = {
|
||
'type': request.data.get('type'),
|
||
'warehouse': request.data.get('warehouse'),
|
||
'source_type': request.data.get('source_type'),
|
||
'source_id': request.data.get('source_id'),
|
||
}
|
||
|
||
if not all([record_data['type'], record_data['warehouse'], record_data['source_type']]):
|
||
return Response({
|
||
'error': '缺少必要参数',
|
||
'message': '请提供 type, warehouse, source_type'
|
||
}, status=status.HTTP_400_BAD_REQUEST)
|
||
|
||
products_data = request.data.get('products', [])
|
||
if not products_data:
|
||
return Response({
|
||
'error': '产品列表不能为空'
|
||
}, status=status.HTTP_400_BAD_REQUEST)
|
||
|
||
if not self.validate_warehouse_visibility(record_data['warehouse'], request):
|
||
return Response({
|
||
'error': f'仓库ID {record_data["warehouse"]} 对当前用户不可见'
|
||
}, status=status.HTTP_403_FORBIDDEN)
|
||
|
||
for p in products_data:
|
||
product_id = p.get('product')
|
||
if not self.validate_product_visibility(product_id, request):
|
||
return Response({
|
||
'error': f'产品ID {product_id} 对当前用户不可见'
|
||
}, status=status.HTTP_403_FORBIDDEN)
|
||
|
||
serializer = serializers.CreateStockChangeSerializer(data={'products': products_data})
|
||
if not serializer.is_valid():
|
||
return Response({
|
||
'error': '产品数据验证失败',
|
||
'details': serializer.errors
|
||
}, status=status.HTTP_400_BAD_REQUEST)
|
||
|
||
try:
|
||
stock_change_record, created_details, created_count = stock_services.create_stock_change_record_with_details(
|
||
merchant=request.user.employee.merchant,
|
||
created_by=request.user,
|
||
type=record_data['type'],
|
||
warehouse_id=record_data['warehouse'],
|
||
source_type=record_data['source_type'],
|
||
source_id=record_data['source_id'],
|
||
products=products_data,
|
||
)
|
||
response_serializer = serializers.CreateStockChangeResponseSerializer({
|
||
'stock_change_record': stock_change_record,
|
||
'details': created_details,
|
||
'message': f'成功创建库存变动记录及 {created_count} 条明细',
|
||
'created_details_count': created_count
|
||
})
|
||
|
||
return Response(response_serializer.data, status=status.HTTP_201_CREATED)
|
||
|
||
except ValueError as e:
|
||
return Response({
|
||
'error': str(e)
|
||
}, status=status.HTTP_400_BAD_REQUEST)
|
||
|
||
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)
|