forked from erp-dev/erp
feat: sales return order and purchase return order
This commit is contained in:
236
api_v1/tests.py
236
api_v1/tests.py
@@ -453,6 +453,242 @@ class SalesOrderAPITestCase(TestCase):
|
||||
self.assertEqual(order.status, business_models.SalesOrderStatusEnum.PENDING)
|
||||
|
||||
|
||||
@override_settings(
|
||||
CELERY_TASK_ALWAYS_EAGER=True,
|
||||
CELERY_TASK_EAGER_PROPAGATES=True,
|
||||
)
|
||||
class PurchaseReturnOrderAPITestCase(TestCase):
|
||||
def setUp(self):
|
||||
self.merchant = Merchant.objects.create(name='采退商户', type=MerchantTypeEnum.FACTORY)
|
||||
self.supplier = Supplier.objects.create(merchant=self.merchant, name='供应商B')
|
||||
self.warehouse_strict = WareHouse.objects.create(
|
||||
merchant=self.merchant,
|
||||
name='采退严进仓',
|
||||
mode=WareHouseModeEnum.RESTRICT_IN,
|
||||
)
|
||||
self.warehouse_relaxed = WareHouse.objects.create(
|
||||
merchant=self.merchant,
|
||||
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='采退品类',
|
||||
product_prefix='RET',
|
||||
)
|
||||
self.product = Product.objects.create(
|
||||
merchant=self.merchant,
|
||||
category=category,
|
||||
name='采退产品',
|
||||
human_id='RET-001',
|
||||
unit=ProductUnitEnum.METER,
|
||||
)
|
||||
self.user = User.objects.create_user(username='return_user', password='pass123')
|
||||
self.employee = Employee.objects.create(
|
||||
merchant=self.merchant,
|
||||
sys_user=self.user,
|
||||
name='仓管员',
|
||||
)
|
||||
self.client = APIClient()
|
||||
self.client.force_authenticate(user=self.user)
|
||||
self.strict_payload = {
|
||||
'supplier': self.supplier.id,
|
||||
'warehouse': self.warehouse_strict.id,
|
||||
'return_date': '2025-11-26',
|
||||
'items': [
|
||||
{
|
||||
'product_id': self.product.id,
|
||||
'numbers': [5, 3],
|
||||
'price': '11.5',
|
||||
'unit': '米',
|
||||
}
|
||||
],
|
||||
'remarks': '采退测试',
|
||||
}
|
||||
self.strict_out_payload = {
|
||||
'supplier': self.supplier.id,
|
||||
'warehouse': self.warehouse_strict_out.id,
|
||||
'return_date': '2025-11-26',
|
||||
'items': [
|
||||
{
|
||||
'product_id': self.product.id,
|
||||
'consume_detail_ids': [201, 202],
|
||||
'quantity': 20,
|
||||
'price': '11.2',
|
||||
'unit': '米',
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
def _create_purchase_return_order(self, payload):
|
||||
body = copy.deepcopy(payload)
|
||||
response = self.client.post('/api/v1/purchase-return-orders/', body, format='json')
|
||||
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||
return response.data['id']
|
||||
|
||||
def test_create_purchase_return_order_success(self):
|
||||
response = self.client.post('/api/v1/purchase-return-orders/', self.strict_payload, format='json')
|
||||
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||
self.assertIn('id', response.data)
|
||||
self.assertEqual(response.data['status'], business_models.PurchaseReturnStatusEnum.PENDING)
|
||||
|
||||
def test_create_purchase_return_requires_consume_ids_in_strict_out(self):
|
||||
payload = copy.deepcopy(self.strict_out_payload)
|
||||
payload['items'][0].pop('consume_detail_ids')
|
||||
response = self.client.post('/api/v1/purchase-return-orders/', payload, format='json')
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
self.assertIn('consume_detail_ids', response.data['error'])
|
||||
|
||||
def test_review_purchase_return_order_approve(self):
|
||||
order_id = self._create_purchase_return_order(self.strict_payload)
|
||||
response = self.client.post(
|
||||
f'/api/v1/purchase-return-orders/{order_id}/review/',
|
||||
{'action': 'approve'},
|
||||
format='json',
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.data['status'], business_models.PurchaseReturnStatusEnum.APPROVED)
|
||||
|
||||
def test_purchase_return_cancel_blocked_after_stock_exists(self):
|
||||
order_id = self._create_purchase_return_order(self.strict_payload)
|
||||
stock_models.StockChangeRecord.objects.create(
|
||||
merchant=self.merchant,
|
||||
type=stock_models.StockChangeTypeEnum.REMOVE,
|
||||
warehouse=self.warehouse_strict,
|
||||
source_type=stock_models.StockChangeSourceEnum.PURCHASE_RETURN,
|
||||
source_id=order_id,
|
||||
)
|
||||
response = self.client.post(
|
||||
f'/api/v1/purchase-return-orders/{order_id}/review/',
|
||||
{'action': 'cancel'},
|
||||
format='json',
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
order = business_models.PurchaseReturnOrder.objects.get(id=order_id)
|
||||
self.assertEqual(order.status, business_models.PurchaseReturnStatusEnum.PENDING)
|
||||
|
||||
|
||||
@override_settings(
|
||||
CELERY_TASK_ALWAYS_EAGER=True,
|
||||
CELERY_TASK_EAGER_PROPAGATES=True,
|
||||
)
|
||||
class SalesReturnOrderAPITestCase(TestCase):
|
||||
def setUp(self):
|
||||
self.merchant = Merchant.objects.create(name='销退商户', type=MerchantTypeEnum.FACTORY)
|
||||
self.customer = Customer.objects.create(
|
||||
merchant=self.merchant,
|
||||
name='销退客户',
|
||||
mobile='13900000000',
|
||||
created_by=None,
|
||||
)
|
||||
self.warehouse_strict = WareHouse.objects.create(
|
||||
merchant=self.merchant,
|
||||
name='销退严进仓',
|
||||
mode=WareHouseModeEnum.RESTRICT_IN,
|
||||
)
|
||||
self.warehouse_relaxed = WareHouse.objects.create(
|
||||
merchant=self.merchant,
|
||||
name='销退宽进仓',
|
||||
mode=WareHouseModeEnum.UNRESTRICTED,
|
||||
)
|
||||
category = ProductCategory.objects.create(
|
||||
merchant=self.merchant,
|
||||
name='销退品类',
|
||||
product_prefix='SR',
|
||||
)
|
||||
self.product = Product.objects.create(
|
||||
merchant=self.merchant,
|
||||
category=category,
|
||||
name='销退产品',
|
||||
human_id='SR-001',
|
||||
unit=ProductUnitEnum.METER,
|
||||
)
|
||||
self.user = User.objects.create_user(username='sales_return_user', password='pass123')
|
||||
self.employee = Employee.objects.create(
|
||||
merchant=self.merchant,
|
||||
sys_user=self.user,
|
||||
name='销退员',
|
||||
)
|
||||
self.client = APIClient()
|
||||
self.client.force_authenticate(user=self.user)
|
||||
self.strict_payload = {
|
||||
'customer': self.customer.id,
|
||||
'warehouse': self.warehouse_strict.id,
|
||||
'return_date': '2025-11-26',
|
||||
'items': [
|
||||
{
|
||||
'product_id': self.product.id,
|
||||
'numbers': [7, 3],
|
||||
'price': '14.5',
|
||||
'unit': '米',
|
||||
}
|
||||
],
|
||||
}
|
||||
self.relaxed_payload = {
|
||||
'customer': self.customer.id,
|
||||
'warehouse': self.warehouse_relaxed.id,
|
||||
'return_date': '2025-11-26',
|
||||
'items': [
|
||||
{
|
||||
'product_id': self.product.id,
|
||||
'quantity': 60,
|
||||
'num_of_rolls': 2,
|
||||
'price': '13.8',
|
||||
}
|
||||
],
|
||||
'remarks': '销退备注',
|
||||
}
|
||||
|
||||
def _create_sales_return_order(self, payload):
|
||||
body = copy.deepcopy(payload)
|
||||
response = self.client.post('/api/v1/sales-return-orders/', body, format='json')
|
||||
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||
return response.data['id']
|
||||
|
||||
def test_create_sales_return_order_success(self):
|
||||
response = self.client.post('/api/v1/sales-return-orders/', self.strict_payload, format='json')
|
||||
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||
self.assertEqual(response.data['status'], business_models.SalesReturnStatusEnum.PENDING)
|
||||
|
||||
def test_create_sales_return_order_invalid_customer(self):
|
||||
payload = {**self.strict_payload, 'customer': 999}
|
||||
response = self.client.post('/api/v1/sales-return-orders/', payload, format='json')
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
def test_review_sales_return_order_approve(self):
|
||||
order_id = self._create_sales_return_order(self.relaxed_payload)
|
||||
response = self.client.post(
|
||||
f'/api/v1/sales-return-orders/{order_id}/review/',
|
||||
{'action': 'approve'},
|
||||
format='json',
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.data['status'], business_models.SalesReturnStatusEnum.APPROVED)
|
||||
|
||||
def test_sales_return_cancel_blocked_after_stock_exists(self):
|
||||
order_id = self._create_sales_return_order(self.strict_payload)
|
||||
stock_models.StockChangeRecord.objects.create(
|
||||
merchant=self.merchant,
|
||||
type=stock_models.StockChangeTypeEnum.ADD,
|
||||
warehouse=self.warehouse_strict,
|
||||
source_type=stock_models.StockChangeSourceEnum.SALES_RETURN,
|
||||
source_id=order_id,
|
||||
)
|
||||
response = self.client.post(
|
||||
f'/api/v1/sales-return-orders/{order_id}/review/',
|
||||
{'action': 'cancel'},
|
||||
format='json',
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
order = business_models.SalesReturnOrder.objects.get(id=order_id)
|
||||
self.assertEqual(order.status, business_models.SalesReturnStatusEnum.PENDING)
|
||||
|
||||
|
||||
class PaymentOrderAPITestCase(TestCase):
|
||||
def setUp(self):
|
||||
self.merchant = Merchant.objects.create(name='付款商户', type=MerchantTypeEnum.FACTORY)
|
||||
|
||||
Reference in New Issue
Block a user