1
0
forked from erp-dev/erp

feat: relaxed in restrict out

This commit is contained in:
2025-11-24 15:57:19 +08:00
parent 113818f73d
commit cd6a2370fc
11 changed files with 840 additions and 5 deletions

View File

@@ -63,6 +63,7 @@ stock_change_views/
```python
# 这些接口保持不变URL 配置无需修改
create_full_stock_change = CreateStockChangeView.as_view()
create_relaxed_stock_change = CreateStockChangeRelaxedView.as_view()
list_stock_changes = ListStockChangesView.as_view()
get_stock_change = GetStockChangeView.as_view()
set_merchant_auto_complete_stock_change = SetMerchantAutoCompleteView.as_view()
@@ -87,6 +88,7 @@ from api_v1.views import stock_change_views
urlpatterns = [
path('stock-change/', stock_change_views.create_full_stock_change),
path('stock-change/relaxed/', stock_change_views.create_relaxed_stock_change),
path('stock-changes/', stock_change_views.list_stock_changes),
path('stock-change/<int:record_id>/', stock_change_views.get_stock_change),
path('set-merchant-auto-complete-stock-change/', stock_change_views.set_merchant_auto_complete_stock_change),
@@ -98,6 +100,7 @@ urlpatterns = [
```python
from api_v1.views.stock_change_views import (
CreateStockChangeView,
CreateStockChangeRelaxedView,
ListStockChangesView,
GetStockChangeView,
SetMerchantAutoCompleteView,
@@ -105,6 +108,7 @@ from api_v1.views.stock_change_views import (
urlpatterns = [
path('stock-change/', CreateStockChangeView.as_view()),
path('stock-change/relaxed/', CreateStockChangeRelaxedView.as_view()),
path('stock-changes/', ListStockChangesView.as_view()),
path('stock-change/<int:record_id>/', GetStockChangeView.as_view()),
path('set-merchant-auto-complete-stock-change/', SetMerchantAutoCompleteView.as_view()),

View File

@@ -5,13 +5,14 @@
"""
from .mixins import StockChangeViewMixin
from .create import CreateStockChangeView
from .create import CreateStockChangeView, CreateStockChangeRelaxedView
from .list import ListStockChangesView
from .detail import GetStockChangeView
from .settings import SetMerchantAutoCompleteView
# 向后兼容:保持原有的函数式接口
create_full_stock_change = CreateStockChangeView.as_view()
create_relaxed_stock_change = CreateStockChangeRelaxedView.as_view()
list_stock_changes = ListStockChangesView.as_view()
get_stock_change = GetStockChangeView.as_view()
set_merchant_auto_complete_stock_change = SetMerchantAutoCompleteView.as_view()
@@ -19,10 +20,12 @@ set_merchant_auto_complete_stock_change = SetMerchantAutoCompleteView.as_view()
__all__ = [
'StockChangeViewMixin',
'CreateStockChangeView',
'CreateStockChangeRelaxedView',
'ListStockChangesView',
'GetStockChangeView',
'SetMerchantAutoCompleteView',
'create_full_stock_change',
'create_relaxed_stock_change',
'list_stock_changes',
'get_stock_change',
'set_merchant_auto_complete_stock_change',

View File

@@ -126,3 +126,89 @@ class CreateStockChangeView(StockChangeViewMixin, views.APIView):
'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)