forked from erp-dev/erp
feat: red flush
This commit is contained in:
326
api_v1/tests.py
326
api_v1/tests.py
@@ -1231,6 +1231,332 @@ class ReceiptOrderAPITestCase(TestCase):
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
self.assertIn('银行账户不存在', response.data['error'])
|
||||
|
||||
|
||||
class BusinessRedFlushAPITestCase(TestCase):
|
||||
def setUp(self):
|
||||
self.merchant = Merchant.objects.create(name='红冲商户', type=MerchantTypeEnum.FACTORY)
|
||||
self.other_merchant = Merchant.objects.create(name='其他红冲商户', type=MerchantTypeEnum.FACTORY)
|
||||
self.supplier = Supplier.objects.create(merchant=self.merchant, name='红冲供应商')
|
||||
self.other_supplier = Supplier.objects.create(merchant=self.other_merchant, name='其他供应商')
|
||||
self.customer = Customer.objects.create(
|
||||
merchant=self.merchant,
|
||||
name='红冲客户',
|
||||
mobile='13800001111',
|
||||
created_by=None,
|
||||
)
|
||||
self.warehouse = WareHouse.objects.create(
|
||||
merchant=self.merchant,
|
||||
name='红冲仓',
|
||||
mode=WareHouseModeEnum.UNRESTRICTED,
|
||||
)
|
||||
category = ProductCategory.objects.create(
|
||||
merchant=self.merchant,
|
||||
name='红冲品类',
|
||||
product_prefix='RF',
|
||||
)
|
||||
self.product = Product.objects.create(
|
||||
merchant=self.merchant,
|
||||
category=category,
|
||||
name='红冲产品',
|
||||
human_id='RF-001',
|
||||
unit=ProductUnitEnum.METER,
|
||||
)
|
||||
self.user = User.objects.create_user(username='red_flush_api_user', password='pass123')
|
||||
self.employee = Employee.objects.create(
|
||||
merchant=self.merchant,
|
||||
sys_user=self.user,
|
||||
name='红冲操作员',
|
||||
status=EmployeeStatusEnum.ACTIVE,
|
||||
)
|
||||
self.other_user = User.objects.create_user(username='other_red_flush_api_user', password='pass123')
|
||||
self.other_employee = Employee.objects.create(
|
||||
merchant=self.other_merchant,
|
||||
sys_user=self.other_user,
|
||||
name='其他红冲操作员',
|
||||
status=EmployeeStatusEnum.ACTIVE,
|
||||
)
|
||||
self.client = APIClient()
|
||||
self.client.force_authenticate(user=self.user)
|
||||
MerchantSetting.objects.filter(
|
||||
merchant=self.merchant,
|
||||
key=MerchantSettingKeyEnum.AUTO_CREATE_STOCK_CHANGE_TASKS,
|
||||
).update(val_bool=False)
|
||||
|
||||
def _items(self, quantity='10', price='5'):
|
||||
return [
|
||||
{
|
||||
'product_id': self.product.id,
|
||||
'quantity': quantity,
|
||||
'num_of_rolls': 1,
|
||||
'price': price,
|
||||
}
|
||||
]
|
||||
|
||||
def _complete_stock_record(self, record_id):
|
||||
record = stock_models.StockChangeRecord.objects.get(id=record_id)
|
||||
stock_services.make_stock_change_completed(record)
|
||||
|
||||
def _create_approved_purchase_order(self):
|
||||
order = services.create_purchase_order(
|
||||
merchant=self.merchant,
|
||||
supplier=self.supplier,
|
||||
order_date=datetime.date(2025, 11, 26),
|
||||
warehouse=self.warehouse,
|
||||
operator=self.employee,
|
||||
items=self._items(),
|
||||
)
|
||||
services.review_purchase_order(
|
||||
purchase_order=order,
|
||||
target_status=business_models.PurchaseOrderStatusEnum.APPROVED,
|
||||
reviewed_by=self.user,
|
||||
)
|
||||
payload = services.create_purchase_order_stock_entries_sync(
|
||||
purchase_order_id=order.id,
|
||||
warehouse_id=self.warehouse.id,
|
||||
items=services._build_stock_flow_items_from_order(order),
|
||||
created_by_id=self.user.id,
|
||||
)
|
||||
self._complete_stock_record(payload['stock_change_record_id'])
|
||||
return order
|
||||
|
||||
def _create_approved_sales_order(self):
|
||||
order = services.create_sales_order(
|
||||
merchant=self.merchant,
|
||||
customer=self.customer,
|
||||
order_date=datetime.date(2025, 11, 26),
|
||||
warehouse=self.warehouse,
|
||||
operator=self.employee,
|
||||
items=self._items(quantity='8', price='7'),
|
||||
)
|
||||
services.review_sales_order(
|
||||
sales_order=order,
|
||||
target_status=business_models.SalesOrderStatusEnum.APPROVED,
|
||||
reviewed_by=self.user,
|
||||
)
|
||||
payload = services.create_sales_order_stock_entries_sync(
|
||||
sales_order_id=order.id,
|
||||
warehouse_id=self.warehouse.id,
|
||||
items=services._build_stock_flow_items_from_order(order),
|
||||
created_by_id=self.user.id,
|
||||
)
|
||||
self._complete_stock_record(payload['stock_change_record_id'])
|
||||
return order
|
||||
|
||||
def _create_approved_purchase_return_order(self):
|
||||
order = services.create_purchase_return_order(
|
||||
merchant=self.merchant,
|
||||
supplier=self.supplier,
|
||||
return_date=datetime.date(2025, 11, 26),
|
||||
warehouse=self.warehouse,
|
||||
operator=self.employee,
|
||||
items=self._items(quantity='6', price='4'),
|
||||
)
|
||||
services.review_purchase_return_order(
|
||||
purchase_return_order=order,
|
||||
target_status=business_models.PurchaseReturnStatusEnum.APPROVED,
|
||||
reviewed_by=self.user,
|
||||
)
|
||||
payload = services.create_purchase_return_order_stock_entries_sync(
|
||||
purchase_return_order_id=order.id,
|
||||
warehouse_id=self.warehouse.id,
|
||||
items=services._build_stock_flow_items_from_order(order),
|
||||
created_by_id=self.user.id,
|
||||
)
|
||||
self._complete_stock_record(payload['stock_change_record_id'])
|
||||
return order
|
||||
|
||||
def _create_approved_sales_return_order(self):
|
||||
order = services.create_sales_return_order(
|
||||
merchant=self.merchant,
|
||||
customer=self.customer,
|
||||
return_date=datetime.date(2025, 11, 26),
|
||||
warehouse=self.warehouse,
|
||||
operator=self.employee,
|
||||
items=self._items(quantity='6', price='4'),
|
||||
)
|
||||
services.review_sales_return_order(
|
||||
sales_return_order=order,
|
||||
target_status=business_models.SalesReturnStatusEnum.APPROVED,
|
||||
reviewed_by=self.user,
|
||||
)
|
||||
payload = services.create_sales_return_order_stock_entries_sync(
|
||||
sales_return_order_id=order.id,
|
||||
warehouse_id=self.warehouse.id,
|
||||
items=services._build_stock_flow_items_from_order(order),
|
||||
created_by_id=self.user.id,
|
||||
)
|
||||
self._complete_stock_record(payload['stock_change_record_id'])
|
||||
return order
|
||||
|
||||
def _create_approved_payment_order(self):
|
||||
order = services.create_payment_order(
|
||||
merchant=self.merchant,
|
||||
supplier=self.supplier,
|
||||
payment_date=datetime.date(2025, 11, 26),
|
||||
amount='30',
|
||||
operator=self.employee,
|
||||
)
|
||||
services.review_payment_order(
|
||||
payment_order=order,
|
||||
target_status=business_models.PaymentOrderStatusEnum.APPROVED,
|
||||
reviewed_by=self.user,
|
||||
)
|
||||
return order
|
||||
|
||||
def _create_approved_receipt_order(self):
|
||||
order = services.create_receipt_order(
|
||||
merchant=self.merchant,
|
||||
customer=self.customer,
|
||||
receipt_date=datetime.date(2025, 11, 26),
|
||||
amount='30',
|
||||
operator=self.employee,
|
||||
)
|
||||
services.review_receipt_order(
|
||||
receipt_order=order,
|
||||
target_status=business_models.ReceiptOrderStatusEnum.APPROVED,
|
||||
reviewed_by=self.user,
|
||||
)
|
||||
return order
|
||||
|
||||
def test_purchase_order_red_flush_requires_reason(self):
|
||||
order = self._create_approved_purchase_order()
|
||||
response = self.client.post(f'/api/v1/purchase-orders/{order.id}/red-flush/', {}, format='json')
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
self.assertIn('reason', response.data)
|
||||
|
||||
def test_purchase_order_red_flush_success(self):
|
||||
order = self._create_approved_purchase_order()
|
||||
response = self.client.post(
|
||||
f'/api/v1/purchase-orders/{order.id}/red-flush/',
|
||||
{'reason': '采购接口红冲'},
|
||||
format='json',
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertTrue(response.data['is_red_flushed'])
|
||||
self.assertIsNotNone(response.data['red_flush_id'])
|
||||
order.refresh_from_db()
|
||||
self.assertEqual(order.status, business_models.PurchaseOrderStatusEnum.APPROVED)
|
||||
self.assertTrue(order.is_red_flushed)
|
||||
|
||||
def test_sales_order_red_flush_success(self):
|
||||
order = self._create_approved_sales_order()
|
||||
response = self.client.post(
|
||||
f'/api/v1/sales-orders/{order.id}/red-flush/',
|
||||
{'reason': '销售接口红冲'},
|
||||
format='json',
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertTrue(response.data['is_red_flushed'])
|
||||
|
||||
def test_purchase_return_order_red_flush_success(self):
|
||||
order = self._create_approved_purchase_return_order()
|
||||
response = self.client.post(
|
||||
f'/api/v1/purchase-return-orders/{order.id}/red-flush/',
|
||||
{'reason': '采购退货接口红冲'},
|
||||
format='json',
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertTrue(response.data['is_red_flushed'])
|
||||
|
||||
def test_sales_return_order_red_flush_success(self):
|
||||
order = self._create_approved_sales_return_order()
|
||||
response = self.client.post(
|
||||
f'/api/v1/sales-return-orders/{order.id}/red-flush/',
|
||||
{'reason': '销售退货接口红冲'},
|
||||
format='json',
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertTrue(response.data['is_red_flushed'])
|
||||
|
||||
def test_payment_order_red_flush_success_and_duplicate_blocked(self):
|
||||
order = self._create_approved_payment_order()
|
||||
response = self.client.post(
|
||||
f'/api/v1/payment-orders/{order.id}/red-flush/',
|
||||
{'reason': '付款接口红冲'},
|
||||
format='json',
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertTrue(response.data['is_red_flushed'])
|
||||
|
||||
duplicate = self.client.post(
|
||||
f'/api/v1/payment-orders/{order.id}/red-flush/',
|
||||
{'reason': '重复红冲'},
|
||||
format='json',
|
||||
)
|
||||
self.assertEqual(duplicate.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
def test_receipt_order_red_flush_success(self):
|
||||
order = self._create_approved_receipt_order()
|
||||
response = self.client.post(
|
||||
f'/api/v1/receipt-orders/{order.id}/red-flush/',
|
||||
{'reason': '收款接口红冲'},
|
||||
format='json',
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertTrue(response.data['is_red_flushed'])
|
||||
|
||||
def test_red_flush_rejects_external_payment_order(self):
|
||||
order = self._create_approved_payment_order()
|
||||
order.is_external_source = True
|
||||
order.external_source_id = 'XT-API-EXTERNAL-001'
|
||||
order.save(update_fields=['is_external_source', 'external_source_id', 'updated_at'])
|
||||
|
||||
response = self.client.post(
|
||||
f'/api/v1/payment-orders/{order.id}/red-flush/',
|
||||
{'reason': '外部付款红冲'},
|
||||
format='json',
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
self.assertIn('外部来源付款单不允许红冲', response.data['error'])
|
||||
order.refresh_from_db()
|
||||
self.assertFalse(order.is_red_flushed)
|
||||
|
||||
def test_red_flush_rejects_external_receipt_order(self):
|
||||
order = self._create_approved_receipt_order()
|
||||
order.external_source_id = 'SK-API-EXTERNAL-001'
|
||||
order.save(update_fields=['external_source_id', 'updated_at'])
|
||||
|
||||
response = self.client.post(
|
||||
f'/api/v1/receipt-orders/{order.id}/red-flush/',
|
||||
{'reason': '外部收款红冲'},
|
||||
format='json',
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
self.assertIn('外部来源收款单不允许红冲', response.data['error'])
|
||||
order.refresh_from_db()
|
||||
self.assertFalse(order.is_red_flushed)
|
||||
|
||||
def test_red_flush_rejects_non_current_merchant_order(self):
|
||||
other_order = services.create_payment_order(
|
||||
merchant=self.other_merchant,
|
||||
supplier=self.other_supplier,
|
||||
payment_date=datetime.date(2025, 11, 26),
|
||||
amount='20',
|
||||
operator=self.other_employee,
|
||||
)
|
||||
response = self.client.post(
|
||||
f'/api/v1/payment-orders/{other_order.id}/red-flush/',
|
||||
{'reason': '跨商户红冲'},
|
||||
format='json',
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
|
||||
|
||||
def test_red_flush_rejects_non_approved_order(self):
|
||||
order = services.create_payment_order(
|
||||
merchant=self.merchant,
|
||||
supplier=self.supplier,
|
||||
payment_date=datetime.date(2025, 11, 26),
|
||||
amount='20',
|
||||
operator=self.employee,
|
||||
)
|
||||
response = self.client.post(
|
||||
f'/api/v1/payment-orders/{order.id}/red-flush/',
|
||||
{'reason': '未审核红冲'},
|
||||
format='json',
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
class CustomerBalanceAPITestCase(TestCase):
|
||||
def setUp(self):
|
||||
self.merchant = Merchant.objects.create(name='余额商户', type=MerchantTypeEnum.FACTORY)
|
||||
|
||||
@@ -155,6 +155,11 @@ urlpatterns = [
|
||||
purchase_views.PurchaseOrderReviewView.as_view(),
|
||||
name="purchase_order_review",
|
||||
),
|
||||
path(
|
||||
"purchase-orders/<int:pk>/red-flush/",
|
||||
purchase_views.PurchaseOrderRedFlushView.as_view(),
|
||||
name="purchase_order_red_flush",
|
||||
),
|
||||
path(
|
||||
"purchase-orders/<int:pk>/bind-stock-change/",
|
||||
purchase_views.PurchaseOrderBindStockChangeView.as_view(),
|
||||
@@ -175,6 +180,11 @@ urlpatterns = [
|
||||
purchase_return_views.PurchaseReturnOrderReviewView.as_view(),
|
||||
name="purchase_return_order_review",
|
||||
),
|
||||
path(
|
||||
"purchase-return-orders/<int:pk>/red-flush/",
|
||||
purchase_return_views.PurchaseReturnOrderRedFlushView.as_view(),
|
||||
name="purchase_return_order_red_flush",
|
||||
),
|
||||
path("sales-orders/", sales_views.SalesOrderView.as_view(), name="sales_orders"),
|
||||
path(
|
||||
"sales-orders/<int:pk>/",
|
||||
@@ -186,6 +196,11 @@ urlpatterns = [
|
||||
sales_views.SalesOrderReviewView.as_view(),
|
||||
name="sales_order_review",
|
||||
),
|
||||
path(
|
||||
"sales-orders/<int:pk>/red-flush/",
|
||||
sales_views.SalesOrderRedFlushView.as_view(),
|
||||
name="sales_order_red_flush",
|
||||
),
|
||||
path(
|
||||
"sales-return-orders/",
|
||||
sales_return_views.SalesReturnOrderView.as_view(),
|
||||
@@ -201,6 +216,11 @@ urlpatterns = [
|
||||
sales_return_views.SalesReturnOrderReviewView.as_view(),
|
||||
name="sales_return_order_review",
|
||||
),
|
||||
path(
|
||||
"sales-return-orders/<int:pk>/red-flush/",
|
||||
sales_return_views.SalesReturnOrderRedFlushView.as_view(),
|
||||
name="sales_return_order_red_flush",
|
||||
),
|
||||
path(
|
||||
"pre-sales-orders/",
|
||||
pre_sales_views.PreSalesOrderView.as_view(),
|
||||
@@ -246,6 +266,11 @@ urlpatterns = [
|
||||
payment_views.PaymentOrderReviewView.as_view(),
|
||||
name="payment_order_review",
|
||||
),
|
||||
path(
|
||||
"payment-orders/<int:pk>/red-flush/",
|
||||
payment_views.PaymentOrderRedFlushView.as_view(),
|
||||
name="payment_order_red_flush",
|
||||
),
|
||||
path(
|
||||
"receipt-orders/",
|
||||
receipt_views.ReceiptOrderView.as_view(),
|
||||
@@ -256,6 +281,11 @@ urlpatterns = [
|
||||
receipt_views.ReceiptOrderReviewView.as_view(),
|
||||
name="receipt_order_review",
|
||||
),
|
||||
path(
|
||||
"receipt-orders/<int:pk>/red-flush/",
|
||||
receipt_views.ReceiptOrderRedFlushView.as_view(),
|
||||
name="receipt_order_red_flush",
|
||||
),
|
||||
path(
|
||||
"customers/<int:customer_id>/balance/",
|
||||
balance_views.CustomerBalanceView.as_view(),
|
||||
|
||||
@@ -20,6 +20,7 @@ class PaymentOrderSerializer(serializers.ModelSerializer):
|
||||
'id', 'supplier', 'supplier_name', 'bank_account', 'bank_account_name',
|
||||
'payment_date', 'amount', 'discount_amount', 'settlement_amount',
|
||||
'operator', 'operator_name', 'status', 'is_external_source', 'external_source_id',
|
||||
'is_red_flushed', 'red_flush_id', 'red_flushed_at',
|
||||
'remarks', 'markup', 'created_at', 'updated_at',
|
||||
]
|
||||
read_only_fields = [
|
||||
@@ -31,6 +32,9 @@ class PaymentOrderSerializer(serializers.ModelSerializer):
|
||||
'status',
|
||||
'is_external_source',
|
||||
'external_source_id',
|
||||
'is_red_flushed',
|
||||
'red_flush_id',
|
||||
'red_flushed_at',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
]
|
||||
@@ -164,3 +168,42 @@ class PaymentOrderReviewView(StockChangeViewMixin, views.APIView):
|
||||
)
|
||||
return Response(PaymentOrderSerializer(refreshed).data, status=status.HTTP_200_OK)
|
||||
|
||||
|
||||
class PaymentOrderRedFlushSerializer(serializers.Serializer):
|
||||
reason = serializers.CharField(required=True, allow_blank=False, trim_whitespace=True)
|
||||
|
||||
|
||||
class PaymentOrderRedFlushView(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
|
||||
try:
|
||||
payment_order = (
|
||||
business_models.PaymentOrder.objects.select_related('supplier', 'operator', 'bank_account')
|
||||
.get(id=pk, merchant=merchant)
|
||||
)
|
||||
except business_models.PaymentOrder.DoesNotExist:
|
||||
return self.not_found_response('付款单不存在')
|
||||
|
||||
serializer = PaymentOrderRedFlushSerializer(data=request.data or {})
|
||||
serializer.is_valid(raise_exception=True)
|
||||
|
||||
try:
|
||||
red_flushed_order = business_services.red_flush_payment_order(
|
||||
merchant=merchant,
|
||||
payment_order=payment_order,
|
||||
red_flushed_by=request.user,
|
||||
reason=serializer.validated_data['reason'],
|
||||
)
|
||||
except ValueError as exc:
|
||||
return Response({'error': str(exc)}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
refreshed = (
|
||||
business_models.PaymentOrder.objects.select_related('supplier', 'operator', 'bank_account')
|
||||
.get(id=red_flushed_order.id)
|
||||
)
|
||||
return Response(PaymentOrderSerializer(refreshed).data, status=status.HTTP_200_OK)
|
||||
|
||||
@@ -53,10 +53,14 @@ class PurchaseOrderSerializer(serializers.ModelSerializer):
|
||||
'id', 'human_id', 'supplier', 'supplier_name', 'purchase_date', 'kind',
|
||||
'total_amount', 'diff_quantity', 'total_quantity',
|
||||
'operator', 'operator_name', 'warehouse', 'warehouse_name',
|
||||
'status', 'remarks', 'created_at', 'updated_at', 'items',
|
||||
'quantity_of_rolls',
|
||||
'status', 'is_red_flushed', 'red_flush_id', 'red_flushed_at',
|
||||
'remarks', 'created_at', 'updated_at', 'items', 'quantity_of_rolls',
|
||||
]
|
||||
read_only_fields = [
|
||||
'id', 'created_at', 'updated_at', 'items', 'supplier_name',
|
||||
'operator_name', 'warehouse_name', 'is_red_flushed',
|
||||
'red_flush_id', 'red_flushed_at',
|
||||
]
|
||||
read_only_fields = ['id', 'created_at', 'updated_at', 'items', 'supplier_name', 'operator_name', 'warehouse_name']
|
||||
|
||||
|
||||
class PurchaseOrderPagination(pagination.LimitOffsetPagination):
|
||||
@@ -258,6 +262,46 @@ class PurchaseOrderReviewView(StockChangeViewMixin, views.APIView):
|
||||
return Response(PurchaseOrderSerializer(refreshed_order).data, status=status.HTTP_200_OK)
|
||||
|
||||
|
||||
class PurchaseOrderRedFlushSerializer(serializers.Serializer):
|
||||
reason = serializers.CharField(required=True, allow_blank=False, trim_whitespace=True)
|
||||
|
||||
|
||||
class PurchaseOrderRedFlushView(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
|
||||
try:
|
||||
purchase_order = business_models.PurchaseOrder.objects.select_related(
|
||||
'supplier', 'operator', 'warehouse'
|
||||
).prefetch_related('items').get(id=pk, merchant=merchant)
|
||||
except business_models.PurchaseOrder.DoesNotExist:
|
||||
return self.not_found_response('采购单不存在')
|
||||
|
||||
serializer = PurchaseOrderRedFlushSerializer(data=request.data or {})
|
||||
serializer.is_valid(raise_exception=True)
|
||||
|
||||
try:
|
||||
red_flushed_order = business_services.red_flush_purchase_order(
|
||||
merchant=merchant,
|
||||
purchase_order=purchase_order,
|
||||
red_flushed_by=request.user,
|
||||
reason=serializer.validated_data['reason'],
|
||||
)
|
||||
except ValueError as exc:
|
||||
return Response({'error': str(exc)}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
refreshed_order = business_models.PurchaseOrder.objects.select_related(
|
||||
'supplier', 'operator', 'warehouse'
|
||||
).prefetch_related('items').get(id=red_flushed_order.id)
|
||||
return Response(PurchaseOrderSerializer(refreshed_order).data, status=status.HTTP_200_OK)
|
||||
|
||||
|
||||
class PurchaseOrderBindStockChangeSerializer(serializers.Serializer):
|
||||
stock_change_record_id = serializers.IntegerField(min_value=1)
|
||||
|
||||
@@ -300,4 +344,3 @@ class PurchaseOrderBindStockChangeView(StockChangeViewMixin, views.APIView):
|
||||
},
|
||||
status=status.HTTP_200_OK,
|
||||
)
|
||||
|
||||
|
||||
@@ -57,12 +57,13 @@ class PurchaseReturnOrderSerializer(serializers.ModelSerializer):
|
||||
'id', 'human_id', 'supplier', 'supplier_name', 'purchase_order', 'return_date',
|
||||
'total_amount', 'diff_quantity', 'total_quantity',
|
||||
'operator', 'operator_name', 'warehouse', 'warehouse_name',
|
||||
'status', 'remarks', 'created_at', 'updated_at', 'items',
|
||||
'quantity_of_rolls',
|
||||
'status', 'is_red_flushed', 'red_flush_id', 'red_flushed_at',
|
||||
'remarks', 'created_at', 'updated_at', 'items', 'quantity_of_rolls',
|
||||
]
|
||||
read_only_fields = [
|
||||
'id', 'created_at', 'updated_at', 'items',
|
||||
'supplier_name', 'operator_name', 'warehouse_name',
|
||||
'is_red_flushed', 'red_flush_id', 'red_flushed_at',
|
||||
]
|
||||
|
||||
|
||||
@@ -260,3 +261,40 @@ class PurchaseReturnOrderReviewView(StockChangeViewMixin, views.APIView):
|
||||
).prefetch_related('items').get(id=purchase_return_order.id)
|
||||
return Response(PurchaseReturnOrderSerializer(refreshed_order).data, status=status.HTTP_200_OK)
|
||||
|
||||
|
||||
class PurchaseReturnOrderRedFlushSerializer(serializers.Serializer):
|
||||
reason = serializers.CharField(required=True, allow_blank=False, trim_whitespace=True)
|
||||
|
||||
|
||||
class PurchaseReturnOrderRedFlushView(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
|
||||
try:
|
||||
purchase_return_order = business_models.PurchaseReturnOrder.objects.select_related(
|
||||
'supplier', 'operator', 'warehouse'
|
||||
).prefetch_related('items').get(id=pk, merchant=merchant)
|
||||
except business_models.PurchaseReturnOrder.DoesNotExist:
|
||||
return self.not_found_response('采购退货单不存在')
|
||||
|
||||
serializer = PurchaseReturnOrderRedFlushSerializer(data=request.data or {})
|
||||
serializer.is_valid(raise_exception=True)
|
||||
|
||||
try:
|
||||
red_flushed_order = business_services.red_flush_purchase_return_order(
|
||||
merchant=merchant,
|
||||
purchase_return_order=purchase_return_order,
|
||||
red_flushed_by=request.user,
|
||||
reason=serializer.validated_data['reason'],
|
||||
)
|
||||
except ValueError as exc:
|
||||
return Response({'error': str(exc)}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
refreshed_order = business_models.PurchaseReturnOrder.objects.select_related(
|
||||
'supplier', 'operator', 'warehouse'
|
||||
).prefetch_related('items').get(id=red_flushed_order.id)
|
||||
return Response(PurchaseReturnOrderSerializer(refreshed_order).data, status=status.HTTP_200_OK)
|
||||
|
||||
@@ -20,6 +20,7 @@ class ReceiptOrderSerializer(serializers.ModelSerializer):
|
||||
'id', 'customer', 'customer_name', 'bank_account', 'bank_account_name',
|
||||
'receipt_date', 'amount', 'discount_amount', 'settlement_amount',
|
||||
'operator', 'operator_name', 'status', 'is_external_source', 'external_source_id',
|
||||
'is_red_flushed', 'red_flush_id', 'red_flushed_at',
|
||||
'remarks', 'markup', 'created_at', 'updated_at',
|
||||
]
|
||||
read_only_fields = [
|
||||
@@ -31,6 +32,9 @@ class ReceiptOrderSerializer(serializers.ModelSerializer):
|
||||
'status',
|
||||
'is_external_source',
|
||||
'external_source_id',
|
||||
'is_red_flushed',
|
||||
'red_flush_id',
|
||||
'red_flushed_at',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
]
|
||||
@@ -164,3 +168,42 @@ class ReceiptOrderReviewView(StockChangeViewMixin, views.APIView):
|
||||
)
|
||||
return Response(ReceiptOrderSerializer(refreshed).data, status=status.HTTP_200_OK)
|
||||
|
||||
|
||||
class ReceiptOrderRedFlushSerializer(serializers.Serializer):
|
||||
reason = serializers.CharField(required=True, allow_blank=False, trim_whitespace=True)
|
||||
|
||||
|
||||
class ReceiptOrderRedFlushView(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
|
||||
try:
|
||||
receipt_order = (
|
||||
business_models.ReceiptOrder.objects.select_related('customer', 'operator', 'bank_account')
|
||||
.get(id=pk, merchant=merchant)
|
||||
)
|
||||
except business_models.ReceiptOrder.DoesNotExist:
|
||||
return self.not_found_response('收款单不存在')
|
||||
|
||||
serializer = ReceiptOrderRedFlushSerializer(data=request.data or {})
|
||||
serializer.is_valid(raise_exception=True)
|
||||
|
||||
try:
|
||||
red_flushed_order = business_services.red_flush_receipt_order(
|
||||
merchant=merchant,
|
||||
receipt_order=receipt_order,
|
||||
red_flushed_by=request.user,
|
||||
reason=serializer.validated_data['reason'],
|
||||
)
|
||||
except ValueError as exc:
|
||||
return Response({'error': str(exc)}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
refreshed = (
|
||||
business_models.ReceiptOrder.objects.select_related('customer', 'operator', 'bank_account')
|
||||
.get(id=red_flushed_order.id)
|
||||
)
|
||||
return Response(ReceiptOrderSerializer(refreshed).data, status=status.HTTP_200_OK)
|
||||
|
||||
@@ -54,10 +54,14 @@ class SalesOrderSerializer(serializers.ModelSerializer):
|
||||
'id', 'human_id', 'customer', 'customer_name', 'sales_date', 'kind',
|
||||
'total_amount', 'diff_quantity', 'total_quantity',
|
||||
'operator', 'operator_name', 'warehouse', 'warehouse_name',
|
||||
'status', 'remarks', 'created_at', 'updated_at', 'items',
|
||||
'quantity_of_rolls',
|
||||
'status', 'is_red_flushed', 'red_flush_id', 'red_flushed_at',
|
||||
'remarks', 'created_at', 'updated_at', 'items', 'quantity_of_rolls',
|
||||
]
|
||||
read_only_fields = [
|
||||
'id', 'created_at', 'updated_at', 'items', 'customer_name',
|
||||
'operator_name', 'warehouse_name', 'is_red_flushed',
|
||||
'red_flush_id', 'red_flushed_at',
|
||||
]
|
||||
read_only_fields = ['id', 'created_at', 'updated_at', 'items', 'customer_name', 'operator_name', 'warehouse_name']
|
||||
|
||||
|
||||
class SalesOrderPagination(pagination.LimitOffsetPagination):
|
||||
@@ -267,3 +271,42 @@ class SalesOrderReviewView(StockChangeViewMixin, views.APIView):
|
||||
).prefetch_related('items').get(id=sales_order.id)
|
||||
return Response(SalesOrderSerializer(refreshed_order).data, status=status.HTTP_200_OK)
|
||||
|
||||
|
||||
class SalesOrderRedFlushSerializer(serializers.Serializer):
|
||||
reason = serializers.CharField(required=True, allow_blank=False, trim_whitespace=True)
|
||||
|
||||
|
||||
class SalesOrderRedFlushView(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
|
||||
try:
|
||||
sales_order = business_models.SalesOrder.objects.select_related(
|
||||
'customer', 'operator', 'warehouse'
|
||||
).prefetch_related('items').get(id=pk, merchant=merchant)
|
||||
except business_models.SalesOrder.DoesNotExist:
|
||||
return self.not_found_response('销售单不存在')
|
||||
|
||||
serializer = SalesOrderRedFlushSerializer(data=request.data or {})
|
||||
serializer.is_valid(raise_exception=True)
|
||||
|
||||
try:
|
||||
red_flushed_order = business_services.red_flush_sales_order(
|
||||
merchant=merchant,
|
||||
sales_order=sales_order,
|
||||
red_flushed_by=request.user,
|
||||
reason=serializer.validated_data['reason'],
|
||||
)
|
||||
except ValueError as exc:
|
||||
return Response({'error': str(exc)}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
refreshed_order = business_models.SalesOrder.objects.select_related(
|
||||
'customer', 'operator', 'warehouse'
|
||||
).prefetch_related('items').get(id=red_flushed_order.id)
|
||||
return Response(SalesOrderSerializer(refreshed_order).data, status=status.HTTP_200_OK)
|
||||
|
||||
@@ -56,11 +56,13 @@ class SalesReturnOrderSerializer(serializers.ModelSerializer):
|
||||
'id', 'human_id', 'customer', 'customer_name', 'sales_order', 'return_date',
|
||||
'total_amount', 'diff_quantity', 'total_quantity', 'quantity_of_rolls',
|
||||
'operator', 'operator_name', 'warehouse', 'warehouse_name',
|
||||
'status', 'remarks', 'created_at', 'updated_at', 'items',
|
||||
'status', 'is_red_flushed', 'red_flush_id', 'red_flushed_at',
|
||||
'remarks', 'created_at', 'updated_at', 'items',
|
||||
]
|
||||
read_only_fields = [
|
||||
'id', 'created_at', 'updated_at', 'items',
|
||||
'customer_name', 'operator_name', 'warehouse_name',
|
||||
'is_red_flushed', 'red_flush_id', 'red_flushed_at',
|
||||
]
|
||||
|
||||
|
||||
@@ -258,3 +260,40 @@ class SalesReturnOrderReviewView(StockChangeViewMixin, views.APIView):
|
||||
).prefetch_related('items').get(id=sales_return_order.id)
|
||||
return Response(SalesReturnOrderSerializer(refreshed_order).data, status=status.HTTP_200_OK)
|
||||
|
||||
|
||||
class SalesReturnOrderRedFlushSerializer(serializers.Serializer):
|
||||
reason = serializers.CharField(required=True, allow_blank=False, trim_whitespace=True)
|
||||
|
||||
|
||||
class SalesReturnOrderRedFlushView(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
|
||||
try:
|
||||
sales_return_order = business_models.SalesReturnOrder.objects.select_related(
|
||||
'customer', 'operator', 'warehouse'
|
||||
).prefetch_related('items').get(id=pk, merchant=merchant)
|
||||
except business_models.SalesReturnOrder.DoesNotExist:
|
||||
return self.not_found_response('销售退货单不存在')
|
||||
|
||||
serializer = SalesReturnOrderRedFlushSerializer(data=request.data or {})
|
||||
serializer.is_valid(raise_exception=True)
|
||||
|
||||
try:
|
||||
red_flushed_order = business_services.red_flush_sales_return_order(
|
||||
merchant=merchant,
|
||||
sales_return_order=sales_return_order,
|
||||
red_flushed_by=request.user,
|
||||
reason=serializer.validated_data['reason'],
|
||||
)
|
||||
except ValueError as exc:
|
||||
return Response({'error': str(exc)}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
refreshed_order = business_models.SalesReturnOrder.objects.select_related(
|
||||
'customer', 'operator', 'warehouse'
|
||||
).prefetch_related('items').get(id=red_flushed_order.id)
|
||||
return Response(SalesReturnOrderSerializer(refreshed_order).data, status=status.HTTP_200_OK)
|
||||
|
||||
Reference in New Issue
Block a user