forked from erp-dev/erp
feat: support pre_sales_order convert to sales_order api
This commit is contained in:
@@ -136,6 +136,8 @@ class PreSalesAllocationAPITestCase(TestCase):
|
||||
)
|
||||
self.assertEqual(delete_resp.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(delete_resp.data['status'], business_models.AllocationRecordStatusEnum.CANCELLED)
|
||||
freeze = stock_models.StockFreeze.objects.get(stock_detail=self.stock_detail)
|
||||
self.assertEqual(freeze.status, stock_models.StockFreezeStatusEnum.CANCELLED)
|
||||
|
||||
def test_create_allocation_with_invalid_stock(self):
|
||||
order = self._create_order()
|
||||
|
||||
@@ -18,6 +18,8 @@ from basic_info.models import (
|
||||
EmployeeStatusEnum,
|
||||
)
|
||||
from business import models as business_models
|
||||
from business import allocation_services
|
||||
from stock import models as stock_models
|
||||
|
||||
|
||||
@override_settings(
|
||||
@@ -37,6 +39,11 @@ class PreSalesOrderAPITestCase(TestCase):
|
||||
name='预销售仓库',
|
||||
mode=WareHouseModeEnum.UNRESTRICTED,
|
||||
)
|
||||
self.warehouse_strict_out = WareHouse.objects.create(
|
||||
merchant=self.merchant,
|
||||
name='预销售严出仓',
|
||||
mode=WareHouseModeEnum.RESTRICT_IN_OUT,
|
||||
)
|
||||
category = ProductCategory.objects.create(
|
||||
merchant=self.merchant,
|
||||
name='预销售品类',
|
||||
@@ -78,6 +85,20 @@ class PreSalesOrderAPITestCase(TestCase):
|
||||
],
|
||||
}
|
||||
|
||||
self.strict_payload = {
|
||||
'customer': self.customer.id,
|
||||
'warehouse': self.warehouse_strict_out.id,
|
||||
'kind': business_models.SalesOrderKindEnum.WHOLESALE,
|
||||
'remarks': '预销售单备注',
|
||||
'items': [
|
||||
{
|
||||
'product_id': self.product.id,
|
||||
'quantity': '10',
|
||||
'unit': '米',
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
def _create(self, payload=None) -> int:
|
||||
resp = self.client.post('/api/v1/pre-sales-orders/', payload or self.payload, format='json')
|
||||
self.assertEqual(resp.status_code, status.HTTP_201_CREATED)
|
||||
@@ -121,3 +142,50 @@ class PreSalesOrderAPITestCase(TestCase):
|
||||
resp = self.client.delete(f'/api/v1/pre-sales-orders/{order_id}/')
|
||||
self.assertEqual(resp.status_code, status.HTTP_204_NO_CONTENT)
|
||||
self.assertFalse(business_models.PreSalesOrder.objects.filter(id=order_id).exists())
|
||||
|
||||
def test_convert_to_sales_order_rejects_non_strict_out(self):
|
||||
order_id = self._create()
|
||||
resp = self.client.post(f'/api/v1/pre-sales-orders/{order_id}/convert-to-sales/', {}, format='json')
|
||||
self.assertEqual(resp.status_code, status.HTTP_403_FORBIDDEN)
|
||||
self.assertIn('仅支持严进严出仓库模式', resp.data.get('error', ''))
|
||||
|
||||
def test_convert_to_sales_order_success(self):
|
||||
resp = self.client.post('/api/v1/pre-sales-orders/', self.strict_payload, format='json')
|
||||
self.assertEqual(resp.status_code, status.HTTP_201_CREATED)
|
||||
order_id = resp.data['id']
|
||||
|
||||
order = business_models.PreSalesOrder.objects.get(id=order_id)
|
||||
item = order.items.first()
|
||||
|
||||
stock_record = stock_models.StockChangeRecord.objects.create(
|
||||
merchant=self.merchant,
|
||||
type=stock_models.StockChangeTypeEnum.ADD,
|
||||
warehouse=self.warehouse_strict_out,
|
||||
source_type=stock_models.StockChangeSourceEnum.PURCHASE,
|
||||
created_by=self.user,
|
||||
)
|
||||
stock_detail = stock_models.StockChangeDetail.objects.create(
|
||||
merchant=self.merchant,
|
||||
product=self.product,
|
||||
unit=ProductUnitEnum.METER,
|
||||
stock_change_record=stock_record,
|
||||
quantity='10',
|
||||
)
|
||||
allocation_services.create_allocation_record(
|
||||
item=item,
|
||||
stock_ids=[stock_detail.id],
|
||||
quantity='10',
|
||||
unit='米',
|
||||
scanned_by=self.employee,
|
||||
)
|
||||
|
||||
convert_resp = self.client.post(
|
||||
f'/api/v1/pre-sales-orders/{order_id}/convert-to-sales/',
|
||||
{},
|
||||
format='json',
|
||||
)
|
||||
self.assertEqual(convert_resp.status_code, status.HTTP_201_CREATED)
|
||||
self.assertIn('id', convert_resp.data)
|
||||
self.assertEqual(convert_resp.data['status'], business_models.SalesOrderStatusEnum.PENDING)
|
||||
sales_order = business_models.SalesOrder.objects.get(id=convert_resp.data['id'])
|
||||
self.assertEqual(sales_order.from_pre_sales_order_id, order_id)
|
||||
|
||||
@@ -91,6 +91,11 @@ urlpatterns = [
|
||||
path('sales-return-orders/<int:pk>/review/', sales_return_views.SalesReturnOrderReviewView.as_view(), name='sales_return_order_review'),
|
||||
path('pre-sales-orders/', pre_sales_views.PreSalesOrderView.as_view(), name='pre_sales_orders'),
|
||||
path('pre-sales-orders/<int:pk>/', pre_sales_views.PreSalesOrderDetailView.as_view(), name='pre_sales_order_detail'),
|
||||
path(
|
||||
'pre-sales-orders/<int:pk>/convert-to-sales/',
|
||||
pre_sales_views.PreSalesOrderConvertToSalesOrderView.as_view(),
|
||||
name='pre_sales_order_convert_to_sales',
|
||||
),
|
||||
path(
|
||||
'pre-sales-order-items/<int:item_id>/allocations/',
|
||||
pre_sales_views.PreSalesOrderItemAllocationView.as_view(),
|
||||
|
||||
@@ -279,6 +279,41 @@ class PreSalesOrderDetailView(StockChangeViewMixin, views.APIView):
|
||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||
|
||||
|
||||
class PreSalesOrderConvertToSalesOrderView(StockChangeViewMixin, views.APIView):
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def post(self, request, pk: int):
|
||||
if not self.check_employee_permission(request):
|
||||
return self.permission_error_response('无权限访问')
|
||||
|
||||
merchant = request.user.employee.merchant
|
||||
employee = request.user.employee
|
||||
|
||||
try:
|
||||
sales_order = pre_order_services.convert_pre_sales_order_to_sales_order(
|
||||
merchant=merchant,
|
||||
pre_sales_order_id=pk,
|
||||
operator=employee,
|
||||
created_by=request.user,
|
||||
)
|
||||
except PermissionError as exc:
|
||||
return Response({'error': str(exc)}, status=status.HTTP_403_FORBIDDEN)
|
||||
except business_models.PreSalesOrder.DoesNotExist:
|
||||
return self.not_found_response('预销售单不存在')
|
||||
except ValueError as exc:
|
||||
return Response({'error': str(exc)}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
return Response(
|
||||
{
|
||||
'id': sales_order.id,
|
||||
'human_id': sales_order.human_id,
|
||||
'status': sales_order.status,
|
||||
'message': '预销售单已转换为销售单,等待审批',
|
||||
},
|
||||
status=status.HTTP_201_CREATED,
|
||||
)
|
||||
|
||||
|
||||
class PreSalesOrderItemAllocationView(StockChangeViewMixin, views.APIView):
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
@@ -336,5 +371,8 @@ class PreSalesOrderItemAllocationDetailView(StockChangeViewMixin, views.APIView)
|
||||
except business_models.AllocationRecord.DoesNotExist:
|
||||
return self.not_found_response('配货记录不存在')
|
||||
|
||||
updated = allocation_services.cancel_allocation_record(record=record)
|
||||
updated = allocation_services.cancel_allocation_record_with_unfreeze(
|
||||
record=record,
|
||||
cancelled_by=request.user,
|
||||
)
|
||||
return Response(AllocationRecordSerializer(updated).data, status=status.HTTP_200_OK)
|
||||
|
||||
Reference in New Issue
Block a user