from decimal import Decimal from django.contrib.auth import get_user_model from django.test import TestCase, override_settings from basic_info.models import ( Merchant, MerchantTypeEnum, Customer, WareHouse, WareHouseModeEnum, ProductCategory, Product, ProductUnitEnum, Employee, EmployeeStatusEnum, ) from business import models as business_models from business import allocation_services from stock import models as stock_models @override_settings( CELERY_TASK_ALWAYS_EAGER=True, CELERY_TASK_EAGER_PROPAGATES=True, ) class AllocationServicesTestCase(TestCase): def setUp(self): self.merchant = Merchant.objects.create(name='配货商户', type=MerchantTypeEnum.FACTORY) self.customer = Customer.objects.create( merchant=self.merchant, name='配货客户', 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='ALC', ) self.product = Product.objects.create( merchant=self.merchant, category=category, name='配货产品', human_id='ALC-001', unit=ProductUnitEnum.METER, ) self.other_product = Product.objects.create( merchant=self.merchant, category=category, name='其他产品', human_id='ALC-002', unit=ProductUnitEnum.METER, ) User = get_user_model() self.user = User.objects.create_user(username='allocation_user', password='pass123') self.employee = Employee.objects.create( merchant=self.merchant, sys_user=self.user, name='配货员', status=EmployeeStatusEnum.ACTIVE, ) self.pre_sales_order = business_models.PreSalesOrder.objects.create( merchant=self.merchant, customer=self.customer, warehouse=self.warehouse, created_by=self.user, operator=self.employee, kind=business_models.SalesOrderKindEnum.WHOLESALE, remarks='预销售单备注', ) self.item = business_models.PreSalesOrderItem.objects.create( pre_sales_order=self.pre_sales_order, product_id=self.product.id, product_name=self.product.name, quantity=Decimal('12.5'), unit='米', remarks='明细备注', ) self.stock_record = stock_models.StockChangeRecord.objects.create( merchant=self.merchant, type=stock_models.StockChangeTypeEnum.ADD, warehouse=self.warehouse, source_type=stock_models.StockChangeSourceEnum.PURCHASE, created_by=self.user, ) self.stock_detail = stock_models.StockChangeDetail.objects.create( merchant=self.merchant, product=self.product, unit=ProductUnitEnum.METER, stock_change_record=self.stock_record, quantity='12.5', ) def test_validate_stock_details_missing(self): with self.assertRaises(ValueError) as ctx: allocation_services.validate_stock_details( merchant=self.merchant, stock_ids=[999999], ) self.assertIn('库存明细不存在', str(ctx.exception)) def test_validate_stock_details_consumed(self): self.stock_detail.is_consumed = True self.stock_detail.save(update_fields=['is_consumed']) with self.assertRaises(ValueError) as ctx: allocation_services.validate_stock_details( merchant=self.merchant, stock_ids=[self.stock_detail.id], ) self.assertIn('库存明细已被消费', str(ctx.exception)) def test_validate_stock_details_frozen(self): stock_models.StockFreeze.objects.create( merchant=self.merchant, product=self.product, warehouse=self.warehouse, stock_detail=self.stock_detail, quantity=Decimal('1'), unit=ProductUnitEnum.METER, status=stock_models.StockFreezeStatusEnum.FROZEN, frozen_by=self.user, ) with self.assertRaises(ValueError) as ctx: allocation_services.validate_stock_details( merchant=self.merchant, stock_ids=[self.stock_detail.id], ) self.assertIn('库存明细已被冻结', str(ctx.exception)) def test_create_allocation_record_product_mismatch(self): other_detail = stock_models.StockChangeDetail.objects.create( merchant=self.merchant, product=self.other_product, unit=ProductUnitEnum.METER, stock_change_record=self.stock_record, quantity='3.0', ) with self.assertRaises(ValueError) as ctx: allocation_services.create_allocation_record( item=self.item, stock_ids=[other_detail.id], quantity='3.0', unit='米', scanned_by=self.employee, ) self.assertIn('库存明细产品不匹配', str(ctx.exception)) def test_create_allocation_record_success(self): record = allocation_services.create_allocation_record( item=self.item, stock_ids=[self.stock_detail.id], quantity='5.5', unit='米', scanned_by=self.employee, remarks='扫码录入', ) self.assertEqual(record.pre_sales_order_item_id, self.item.id) self.assertEqual(record.stock_ids, str(self.stock_detail.id)) self.assertEqual(record.stock_id_list, [self.stock_detail.id]) self.assertEqual(record.status, business_models.AllocationRecordStatusEnum.ACTIVE) freeze_qs = stock_models.StockFreeze.objects.filter(stock_detail=self.stock_detail) self.assertEqual(freeze_qs.count(), 1) self.assertEqual(freeze_qs.first().status, stock_models.StockFreezeStatusEnum.FROZEN) def test_cancel_allocation_record(self): record = allocation_services.create_allocation_record( item=self.item, stock_ids=[self.stock_detail.id], quantity='2.0', unit='米', scanned_by=self.employee, ) updated = allocation_services.cancel_allocation_record_with_unfreeze( record=record, cancelled_by=self.user, reason='测试撤销', ) self.assertEqual(updated.status, business_models.AllocationRecordStatusEnum.CANCELLED) freeze = stock_models.StockFreeze.objects.get(stock_detail=self.stock_detail) self.assertEqual(freeze.status, stock_models.StockFreezeStatusEnum.CANCELLED)