from decimal import Decimal from typing import List, Dict, Any from django.contrib.auth import get_user_model from django.test import TestCase, override_settings from django.utils import timezone from unittest.mock import patch, MagicMock from basic_info import models as basic_models from stock import models as stock_models from business import models as business_models, services, tasks def create_basic_fixtures(): merchant = basic_models.Merchant.objects.create( name='测试商户', type=basic_models.MerchantTypeEnum.FACTORY, ) supplier = basic_models.Supplier.objects.create( merchant=merchant, name='测试供应商', ) warehouse = basic_models.WareHouse.objects.create( merchant=merchant, name='主仓', mode=basic_models.WareHouseModeEnum.RESTRICT_IN, ) category = basic_models.ProductCategory.objects.create( merchant=merchant, name='面料', product_prefix='FAB', ) product = basic_models.Product.objects.create( merchant=merchant, category=category, name='测试面料', human_id='FAB-001', unit=basic_models.ProductUnitEnum.METER, ) return merchant, supplier, warehouse, product class PurchaseOrderServiceTestCase(TestCase): def setUp(self): self.merchant, self.supplier, self.warehouse, self.product = create_basic_fixtures() User = get_user_model() self.user = User.objects.create_user(username='creator', password='pass123') self.items: List[Dict[str, Any]] = [ {'product_id': self.product.id, 'quantities': ['10.00', '5.00']}, ] def test_create_purchase_order_triggers_task(self): with patch('business.services.create_purchase_order_stock_entries.delay') as mock_delay: purchase_order = services.create_purchase_order( merchant=self.merchant, supplier=self.supplier, order_date=timezone.now().date(), total_amount=Decimal('100.00'), warehouse_id=self.warehouse.id, items=self.items, remarks='自动化测试', created_by=self.user, ) self.assertIsInstance(purchase_order, business_models.PurchaseOrder) mock_delay.assert_called_once_with( purchase_order_id=purchase_order.id, warehouse_id=self.warehouse.id, items=self.items, created_by_id=self.user.id, ) def test_create_purchase_order_without_items_raises(self): with self.assertRaises(ValueError): services.create_purchase_order( merchant=self.merchant, supplier=self.supplier, order_date=timezone.now().date(), total_amount=Decimal('50.00'), warehouse_id=self.warehouse.id, items=[], ) @override_settings( CELERY_TASK_ALWAYS_EAGER=True, CELERY_TASK_EAGER_PROPAGATES=True, ) class PurchaseOrderStockTaskTestCase(TestCase): def setUp(self): self.merchant, self.supplier, self.warehouse, self.product = create_basic_fixtures() User = get_user_model() self.user = User.objects.create_user(username='task-user', password='pass123') self.purchase_order = business_models.PurchaseOrder.objects.create( merchant=self.merchant, supplier=self.supplier, order_date=timezone.now().date(), total_amount=Decimal('100.00'), ) self.items = [{'product_id': self.product.id, 'quantities': ['8.00']}] def test_task_calls_stock_flow_service(self): with patch('business.tasks.StockFlowService') as mock_flow_cls: mock_instance = mock_flow_cls.return_value mock_instance.stock_in.return_value = (MagicMock(id=321), [], 2) async_result = tasks.create_purchase_order_stock_entries.delay( purchase_order_id=self.purchase_order.id, warehouse_id=self.warehouse.id, items=self.items, created_by_id=self.user.id, ) payload = async_result.get(timeout=5) mock_flow_cls.assert_called_once_with(merchant=self.merchant, created_by=self.user) mock_instance.stock_in.assert_called_once_with( warehouse_id=self.warehouse.id, source_type=stock_models.StockChangeSourceEnum.PURCHASE, source_id=self.purchase_order.id, items=self.items, ) self.assertEqual(payload['purchase_order_id'], self.purchase_order.id) self.assertEqual(payload['stock_change_record_id'], 321) from django.test import TestCase # Create your tests here.