import copy
import shutil
import tempfile
import datetime
from pathlib import Path
from unittest.mock import patch
from decimal import Decimal

from django.test import TestCase, override_settings
from django.contrib.auth.models import User, Permission
from rest_framework.test import APIClient
from rest_framework import status
from basic_info.models import (
    BankAccount,
    Customer,
    Employee,
    EmployeeStatusEnum,
    Merchant,
    MerchantTypeEnum,
    MerchantSetting,
    MerchantSettingKeyEnum,
    Product,
    ProductCategory,
    ProductUnitEnum,
    Supplier,
    UserProfile,
    WareHouse,
    WareHouseModeEnum,
)
from business import models as business_models, services
from stock import models as stock_models
from stock import services as stock_services
from api_v1 import tasks
from printing import models as printing_models


class UserCreationAPITestCase(TestCase):
    """测试用户创建 API"""

    def setUp(self):
        """设置测试数据"""
        # 创建商户
        self.merchant = Merchant.objects.create(
            name='测试商户',
            type=1  # 假设1是有效的MerchantType
        )
        
        # 创建管理员用户
        self.admin_user = User.objects.create_user(
            username='admin',
            password='adminpass123',
            is_staff=True
        )
        # 授予创建用户所需的权限
        add_user_perm = Permission.objects.get(codename='add_user')
        self.admin_user.user_permissions.add(add_user_perm)
        self.admin_user.save()
        
        # 设置 API 客户端
        self.client = APIClient()
        self.client.force_authenticate(user=self.admin_user)

    def test_create_user_with_profile(self):
        """测试创建用户和用户资料"""
        data = {
            'username': 'testuser',
            'email': 'test@example.com',
            'password': 'testpass123',
            'is_staff': False,
            'description': '测试用户资料',
            'merchant_id': self.merchant.id
        }
        response = self.client.post('/api/v1/users/create/', data, format='json')
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        
        # 验证返回的数据
        self.assertIn('user', response.data)
        self.assertIn('profile', response.data)
        self.assertEqual(response.data['user']['username'], 'testuser')
        self.assertEqual(response.data['user']['email'], 'test@example.com')
        self.assertEqual(response.data['user']['is_staff'], False)
        self.assertEqual(response.data['profile']['description'], '测试用户资料')
        self.assertEqual(response.data['profile']['merchant'], self.merchant.id)
        
        # 验证数据库中的用户
        user = User.objects.get(username='testuser')
        self.assertEqual(user.email, 'test@example.com')
        self.assertFalse(user.is_staff)
        
        # 验证数据库中的用户资料
        profile = UserProfile.objects.get(user=user)
        self.assertEqual(profile.merchant, self.merchant)
        self.assertEqual(profile.description, '测试用户资料')

    def test_create_user_with_duplicate_username(self):
        """测试创建用户时使用重复的用户名"""
        # 先创建一个用户
        User.objects.create_user(username='existinguser', password='pass123')
        
        data = {
            'username': 'existinguser',  # 重复的用户名
            'password': 'testpass123',
            'merchant_id': self.merchant.id
        }
        response = self.client.post('/api/v1/users/create/', data, format='json')
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertIn('username', response.data)

    def test_create_user_with_invalid_merchant(self):
        """测试创建用户时使用无效的merchant_id"""
        data = {
            'username': 'testuser',
            'password': 'testpass123',
            'merchant_id': 999  # 不存在的merchant_id
        }
        response = self.client.post('/api/v1/users/create/', data, format='json')
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertIn('merchant_id', response.data)

    def test_create_user_unauthenticated(self):
        """测试未认证用户创建用户"""
        self.client.force_authenticate(user=None)
        
        data = {
            'username': 'testuser',
            'password': 'testpass123',
            'merchant_id': self.merchant.id
        }
        response = self.client.post('/api/v1/users/create/', data, format='json')
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

    def test_create_user_with_short_password(self):
        """测试创建用户时密码过短"""
        data = {
            'username': 'testuser',
            'password': '123',  # 密码过短
            'merchant_id': self.merchant.id
        }
        response = self.client.post('/api/v1/users/create/', data, format='json')
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertIn('password', response.data)


@override_settings(
    CELERY_TASK_ALWAYS_EAGER=True,
    CELERY_TASK_EAGER_PROPAGATES=True,
)
class PurchaseOrderAPITestCase(TestCase):
    """采购单 API 测试"""

    def setUp(self):
        self.merchant = Merchant.objects.create(name='PO商户', type=MerchantTypeEnum.FACTORY)
        self.supplier = Supplier.objects.create(merchant=self.merchant, name='供应商A')
        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='FAB',
        )
        self.product = Product.objects.create(
            merchant=self.merchant,
            category=category,
            name='产品1',
            human_id='FAB-001',
            unit=ProductUnitEnum.METER,
        )
        self.user = User.objects.create_user(username='po_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,
            'order_date': '2025-11-26',
            'items': [
                {
                    'product_id': self.product.id,
                    'numbers': [10, 5],
                    'price': '12.5',
                    'unit': '米',
                }
            ],
            'remarks': '接口测试',
        }
        self.relaxed_payload = {
            'supplier': self.supplier.id,
            'warehouse': self.warehouse_relaxed.id,
            'order_date': '2025-11-26',
            'items': [
                {
                    'product_id': self.product.id,
                    'quantity': 120,
                    'num_of_rolls': 3,
                    'price': '10.5',
                }
            ],
        }

    def _create_purchase_order(self, payload):
        body = copy.deepcopy(payload)
        with patch('business.services.create_purchase_order_stock_entries.delay'):
            response = self.client.post('/api/v1/purchase-orders/', body, format='json')
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        return response.data['id']

    def test_create_purchase_order_success_strict(self):
        with patch('business.services.create_purchase_order_stock_entries.delay') as mock_delay:
            response = self.client.post('/api/v1/purchase-orders/', self.strict_payload, format='json')

        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertIn('id', response.data)
        self.assertIn('human_id', response.data)
        self.assertEqual(response.data['status'], 1)
        self.assertEqual(response.data['balance_before_snapshot'], Decimal('0'))
        self.assertIn('等待审批', response.data['message'])
        order = business_models.PurchaseOrder.objects.get(id=response.data['id'])
        self.assertEqual(order.balance_before_snapshot, Decimal('0'))
        expected_human_id = f"CG{order.created_at.strftime('%Y%m%d')}{order.id:06d}"
        self.assertEqual(response.data['human_id'], expected_human_id)
        mock_delay.assert_not_called()

    def test_create_purchase_order_with_pre_purchase_binding(self):
        pre_order = business_models.PrePurchaseOrder.objects.create(
            merchant=self.merchant,
            supplier=self.supplier,
            warehouse=self.warehouse_strict,
            operator=self.employee,
            created_by=self.user,
        )
        payload = {**self.strict_payload, 'from_pre_purchase_order_id': pre_order.id}
        with patch('business.services.create_purchase_order_stock_entries.delay'):
            response = self.client.post('/api/v1/purchase-orders/', payload, format='json')

        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        order = business_models.PurchaseOrder.objects.get(id=response.data['id'])
        self.assertEqual(order.from_pre_purchase_order_id, pre_order.id)

    def test_bind_purchase_order_stock_change_record(self):
        MerchantSetting.objects.filter(
            merchant=self.merchant,
            key=MerchantSettingKeyEnum.AUTO_CREATE_STOCK_CHANGE_TASKS,
        ).update(val_bool=False)

        order_id = self._create_purchase_order(self.strict_payload)
        record = stock_models.StockChangeRecord.objects.create(
            merchant=self.merchant,
            type=stock_models.StockChangeTypeEnum.ADD,
            warehouse=self.warehouse_strict,
            source_type=stock_models.StockChangeSourceEnum.PURCHASE,
            created_by=self.user,
        )

        resp = self.client.post(
            f'/api/v1/purchase-orders/{order_id}/bind-stock-change/',
            {'stock_change_record_id': record.id},
            format='json',
        )
        self.assertEqual(resp.status_code, status.HTTP_200_OK)
        record.refresh_from_db()
        self.assertEqual(record.source_id, order_id)

    def test_create_purchase_order_invalid_supplier(self):
        payload = {**self.strict_payload, 'supplier': 999}
        response = self.client.post('/api/v1/purchase-orders/', payload, format='json')
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertIn('不存在', response.data['error'])

    def test_create_purchase_order_unauthenticated(self):
        self.client.force_authenticate(user=None)
        response = self.client.post('/api/v1/purchase-orders/', self.strict_payload, format='json')
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

    def test_create_purchase_order_relaxed_mode(self):
        with patch('business.services.create_purchase_order_stock_entries.delay') as mock_delay:
            response = self.client.post('/api/v1/purchase-orders/', self.relaxed_payload, format='json')
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertIn('human_id', response.data)
        self.assertEqual(response.data['status'], 1)
        order = business_models.PurchaseOrder.objects.get(id=response.data['id'])
        expected_human_id = f"CG{order.created_at.strftime('%Y%m%d')}{order.id:06d}"
        self.assertEqual(response.data['human_id'], expected_human_id)
        mock_delay.assert_not_called()

    def test_mode_mismatch_raises(self):
        payload = {**self.relaxed_payload}
        payload['warehouse'] = self.warehouse_strict.id  # 严进仓却传宽进参数
        response = self.client.post('/api/v1/purchase-orders/', payload, format='json')
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertIn('numbers', response.data['error'])

    def test_review_purchase_order_requires_action(self):
        order_id = self._create_purchase_order(self.strict_payload)
        response = self.client.post(f'/api/v1/purchase-orders/{order_id}/review/', {}, format='json')
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertIn('action', response.data)

    def test_review_purchase_order_approve_success(self):
        order_id = self._create_purchase_order(self.strict_payload)
        response = self.client.post(
            f'/api/v1/purchase-orders/{order_id}/review/',
            {'action': 'approve'},
            format='json',
        )
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data['status'], business_models.PurchaseOrderStatusEnum.APPROVED)
        order = business_models.PurchaseOrder.objects.get(id=order_id)
        self.assertEqual(order.status, business_models.PurchaseOrderStatusEnum.APPROVED)

    def test_review_purchase_order_cancel_blocked_after_stock_exists(self):
        order_id = self._create_purchase_order(self.relaxed_payload)
        stock_models.StockChangeRecord.objects.create(
            merchant=self.merchant,
            type=stock_models.StockChangeTypeEnum.ADD,
            warehouse=self.warehouse_relaxed,
            source_type=stock_models.StockChangeSourceEnum.PURCHASE,
            source_id=order_id,
        )
        response = self.client.post(
            f'/api/v1/purchase-orders/{order_id}/review/',
            {'action': 'cancel'},
            format='json',
        )
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        order = business_models.PurchaseOrder.objects.get(id=order_id)
        self.assertEqual(order.status, business_models.PurchaseOrderStatusEnum.PENDING)

    def test_update_purchase_order_success(self):
        order_id = self._create_purchase_order(self.strict_payload)
        pre_order = business_models.PrePurchaseOrder.objects.create(
            merchant=self.merchant,
            supplier=self.supplier,
            warehouse=self.warehouse_relaxed,
            operator=self.employee,
            created_by=self.user,
        )
        payload = {
            'supplier': self.supplier.id,
            'warehouse': self.warehouse_relaxed.id,
            'order_date': '2025-12-01',
            'items': [
                {
                    'product_id': self.product.id,
                    'quantity': 200,
                    'num_of_rolls': 4,
                    'price': '12.88',
                }
            ],
            'remarks': '更新采购单',
            'from_pre_purchase_order_id': pre_order.id,
        }
        response = self.client.put(f'/api/v1/purchase-orders/{order_id}/', payload, format='json')
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data['remarks'], '更新采购单')
        updated_order = business_models.PurchaseOrder.objects.get(id=order_id)
        self.assertEqual(updated_order.from_pre_purchase_order_id, pre_order.id)

    def test_update_purchase_order_rejects_non_pending(self):
        order_id = self._create_purchase_order(self.relaxed_payload)
        self.client.post(
            f'/api/v1/purchase-orders/{order_id}/review/',
            {'action': 'approve'},
            format='json',
        )
        payload = copy.deepcopy(self.relaxed_payload)
        response = self.client.put(f'/api/v1/purchase-orders/{order_id}/', payload, format='json')
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertIn('不可修改', response.data['error'])


@override_settings(
    CELERY_TASK_ALWAYS_EAGER=True,
    CELERY_TASK_EAGER_PROPAGATES=True,
)
class SalesOrderAPITestCase(TestCase):
    """销售单 API 测试"""

    def setUp(self):
        self.merchant = Merchant.objects.create(name='销售商户', type=MerchantTypeEnum.FACTORY)
        self.customer = Customer.objects.create(
            merchant=self.merchant,
            name='客户A',
            mobile='13800000000',
            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,
        )
        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='SAL',
        )
        self.product = Product.objects.create(
            merchant=self.merchant,
            category=category,
            name='销售产品1',
            human_id='SAL-001',
            unit=ProductUnitEnum.METER,
        )
        self.printing_order_for_sales = printing_models.PrintingOrder.objects.create(
            customer=self.customer,
            fabric='棉布',
            width='150cm',
        )
        self.printing_job_for_sales = printing_models.PrintingJob.objects.create(
            printing_order=self.printing_order_for_sales,
            product=self.product,
            quantity=50,
            unit='米',
        )
        self.user = User.objects.create_user(username='sales_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_in_payload = {
            'customer': self.customer.id,
            'warehouse': self.warehouse_strict.id,
            'order_date': '2025-11-26',
            'items': [
                {
                    'product_id': self.product.id,
                    'numbers': [8, 4],
                    'price': '15.0',
                    'unit': '米',
                }
            ],
            'remarks': '销售接口测试',
        }
        self.strict_out_payload = {
            'customer': self.customer.id,
            'warehouse': self.warehouse_strict_out.id,
            'order_date': '2025-11-26',
            'items': [
                {
                    'product_id': self.product.id,
                    'consume_detail_ids': [101, 102],
                    'quantity': 30,
                    'price': '18.5',
                    'unit': '米',
                }
            ],
        }
        self.relaxed_payload = {
            'customer': self.customer.id,
            'warehouse': self.warehouse_relaxed.id,
            'order_date': '2025-11-26',
            'items': [
                {
                    'product_id': self.product.id,
                    'quantity': 90,
                    'num_of_rolls': 3,
                    'price': '16.5',
                }
            ],
        }

    def _create_sales_order(self, payload):
        body = copy.deepcopy(payload)
        with patch('business.services.create_sales_order_stock_entries.delay'):
            response = self.client.post('/api/v1/sales-orders/', body, format='json')
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        return response.data['id']

    def test_create_sales_order_success_strict(self):
        with patch('business.services.create_sales_order_stock_entries.delay') as mock_delay:
            response = self.client.post('/api/v1/sales-orders/', self.strict_in_payload, format='json')

        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertIn('id', response.data)
        self.assertIn('human_id', response.data)
        order = business_models.SalesOrder.objects.get(id=response.data['id'])
        expected_human_id = f"XS{order.created_at.strftime('%Y%m%d')}{order.id:06d}"
        self.assertEqual(response.data['human_id'], expected_human_id)
        self.assertEqual(response.data['status'], business_models.SalesOrderStatusEnum.PENDING)
        self.assertEqual(response.data['balance_before_snapshot'], Decimal('0'))
        self.assertEqual(order.balance_before_snapshot, Decimal('0'))
        mock_delay.assert_not_called()

    def test_create_sales_order_invalid_customer(self):
        payload = {**self.strict_in_payload, 'customer': 999}
        response = self.client.post('/api/v1/sales-orders/', payload, format='json')
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertIn('不存在', response.data['error'])

    def test_create_sales_order_relaxed_mode(self):
        with patch('business.services.create_sales_order_stock_entries.delay') as mock_delay:
            response = self.client.post('/api/v1/sales-orders/', self.relaxed_payload, format='json')
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(response.data['status'], business_models.SalesOrderStatusEnum.PENDING)
        mock_delay.assert_not_called()

    def test_create_sales_order_strict_out_success(self):
        with patch('business.services.create_sales_order_stock_entries.delay') as mock_delay:
            response = self.client.post('/api/v1/sales-orders/', self.strict_out_payload, format='json')
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(response.data['status'], business_models.SalesOrderStatusEnum.PENDING)
        mock_delay.assert_not_called()

    def test_create_sales_order_strict_out_requires_consume_ids(self):
        payload = copy.deepcopy(self.strict_out_payload)
        payload['items'][0].pop('consume_detail_ids')
        response = self.client.post('/api/v1/sales-orders/', payload, format='json')
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertIn('consume_detail_ids', response.data['error'])

    def test_update_sales_order_success(self):
        order_id = self._create_sales_order(self.strict_in_payload)
        payload = {
            'customer': self.customer.id,
            'warehouse': self.warehouse_relaxed.id,
            'order_date': '2025-12-01',
            'items': [
                {
                    'product_id': self.product.id,
                    'quantity': 100,
                    'num_of_rolls': 2,
                    'price': '22.5',
                }
            ],
            'remarks': '更新后的销售单',
        }
        response = self.client.put(f'/api/v1/sales-orders/{order_id}/', payload, format='json')
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data['remarks'], '更新后的销售单')
        self.assertEqual(response.data['quantity_of_rolls'], [[]])

    def test_get_sales_order_detail_includes_human_id(self):
        order_id = self._create_sales_order(self.strict_in_payload)
        response = self.client.get(f'/api/v1/sales-orders/{order_id}/')
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data['id'], order_id)
        self.assertIn('human_id', response.data)
        order = business_models.SalesOrder.objects.get(id=order_id)
        expected_human_id = f"XS{order.created_at.strftime('%Y%m%d')}{order.id:06d}"
        self.assertEqual(response.data['human_id'], expected_human_id)

    def test_get_sales_order_detail_includes_order_quantity_on_items(self):
        payload = copy.deepcopy(self.strict_in_payload)
        payload['items'][0]['order_quantity'] = 88
        order_id = self._create_sales_order(payload)

        response = self.client.get(f'/api/v1/sales-orders/{order_id}/')
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertIn('items', response.data)
        self.assertEqual(len(response.data['items']), 1)
        self.assertIn('order_quantity', response.data['items'][0])
        self.assertEqual(response.data['items'][0]['order_quantity'], 88)

    def test_update_sales_order_rejects_non_pending(self):
        order_id = self._create_sales_order(self.strict_in_payload)
        self.client.post(
            f'/api/v1/sales-orders/{order_id}/review/',
            {'action': 'approve'},
            format='json',
        )
        payload = {
            'customer': self.customer.id,
            'warehouse': self.warehouse_relaxed.id,
            'order_date': '2025-12-02',
            'items': [
                {
                    'product_id': self.product.id,
                    'quantity': 10,
                    'num_of_rolls': 1,
                    'price': '30',
                }
            ],
        }
        response = self.client.put(f'/api/v1/sales-orders/{order_id}/', payload, format='json')
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertIn('不可修改', response.data['error'])

    def test_create_sales_order_with_printing_job_binding(self):
        payload = copy.deepcopy(self.relaxed_payload)
        payload['items'][0]['printing_job'] = self.printing_job_for_sales.id
        with patch('business.services.create_sales_order_stock_entries.delay'):
            response = self.client.post('/api/v1/sales-orders/', payload, format='json')
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        order = business_models.SalesOrder.objects.get(id=response.data['id'])
        item = order.items.first()
        self.assertEqual(item.printing_job_id, self.printing_job_for_sales.id)

    def test_review_sales_order_requires_action(self):
        order_id = self._create_sales_order(self.strict_in_payload)
        response = self.client.post(f'/api/v1/sales-orders/{order_id}/review/', {}, format='json')
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertIn('action', response.data)

    def test_review_sales_order_approve_success(self):
        order_id = self._create_sales_order(self.strict_in_payload)
        response = self.client.post(
            f'/api/v1/sales-orders/{order_id}/review/',
            {'action': 'approve'},
            format='json',
        )
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data['status'], business_models.SalesOrderStatusEnum.APPROVED)
        order = business_models.SalesOrder.objects.get(id=order_id)
        self.assertEqual(order.status, business_models.SalesOrderStatusEnum.APPROVED)

    def test_review_sales_order_cancel_blocked_after_stock_exists(self):
        order_id = self._create_sales_order(self.relaxed_payload)
        stock_models.StockChangeRecord.objects.create(
            merchant=self.merchant,
            type=stock_models.StockChangeTypeEnum.REMOVE,
            warehouse=self.warehouse_relaxed,
            source_type=stock_models.StockChangeSourceEnum.SALES,
            source_id=order_id,
        )
        response = self.client.post(
            f'/api/v1/sales-orders/{order_id}/review/',
            {'action': 'cancel'},
            format='json',
        )
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        order = business_models.SalesOrder.objects.get(id=order_id)
        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.assertIn('human_id', response.data)
        self.assertEqual(response.data['status'], business_models.PurchaseReturnStatusEnum.PENDING)
        self.assertEqual(response.data['balance_before_snapshot'], Decimal('0'))
        order = business_models.PurchaseReturnOrder.objects.get(id=response.data['id'])
        self.assertEqual(order.balance_before_snapshot, Decimal('0'))
        expected_human_id = f"CT{order.created_at.strftime('%Y%m%d')}{order.id:06d}"
        self.assertEqual(response.data['human_id'], expected_human_id)

    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)

    def test_update_purchase_return_order_success(self):
        order_id = self._create_purchase_return_order(self.strict_payload)
        payload = {
            'supplier': self.supplier.id,
            'warehouse': self.warehouse_relaxed.id,
            'return_date': '2025-12-05',
            'items': [
                {
                    'product_id': self.product.id,
                    'quantity': 50,
                    'num_of_rolls': 2,
                    'price': '12.0',
                }
            ],
            'remarks': '更新采退',
        }
        response = self.client.put(f'/api/v1/purchase-return-orders/{order_id}/', payload, format='json')
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data['remarks'], '更新采退')

    def test_update_purchase_return_order_rejects_non_pending(self):
        order_id = self._create_purchase_return_order(self.strict_payload)
        self.client.post(
            f'/api/v1/purchase-return-orders/{order_id}/review/',
            {'action': 'approve'},
            format='json',
        )
        payload = copy.deepcopy(self.strict_payload)
        response = self.client.put(f'/api/v1/purchase-return-orders/{order_id}/', payload, format='json')
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertIn('不可修改', response.data['error'])


@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.assertIn('human_id', response.data)
        self.assertEqual(response.data['status'], business_models.SalesReturnStatusEnum.PENDING)
        self.assertEqual(response.data['balance_before_snapshot'], Decimal('0'))
        order = business_models.SalesReturnOrder.objects.get(id=response.data['id'])
        self.assertEqual(order.balance_before_snapshot, Decimal('0'))
        expected_human_id = f"XT{order.created_at.strftime('%Y%m%d')}{order.id:06d}"
        self.assertEqual(response.data['human_id'], expected_human_id)

    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)

    def test_update_sales_return_order_success(self):
        order_id = self._create_sales_return_order(self.strict_payload)
        payload = {
            'customer': self.customer.id,
            'warehouse': self.warehouse_relaxed.id,
            'return_date': '2025-12-02',
            'items': [
                {
                    'product_id': self.product.id,
                    'quantity': 40,
                    'num_of_rolls': 2,
                    'price': '18.0',
                }
            ],
            'remarks': '更新销退',
        }
        response = self.client.put(f'/api/v1/sales-return-orders/{order_id}/', payload, format='json')
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data['remarks'], '更新销退')

    def test_update_sales_return_order_rejects_non_pending(self):
        order_id = self._create_sales_return_order(self.strict_payload)
        self.client.post(
            f'/api/v1/sales-return-orders/{order_id}/review/',
            {'action': 'approve'},
            format='json',
        )
        payload = {
            'customer': self.customer.id,
            'warehouse': self.warehouse_relaxed.id,
            'return_date': '2025-12-03',
            'items': [
                {
                    'product_id': self.product.id,
                    'quantity': 20,
                    'num_of_rolls': 1,
                    'price': '17',
                }
            ],
        }
        response = self.client.put(f'/api/v1/sales-return-orders/{order_id}/', payload, format='json')
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertIn('不可修改', response.data['error'])


@override_settings(
    CELERY_TASK_ALWAYS_EAGER=True,
    CELERY_TASK_EAGER_PROPAGATES=True,
)
class StockChangeOffsetAPITestCase(TestCase):
    def setUp(self):
        self.merchant = Merchant.objects.create(name='库存商户', type=MerchantTypeEnum.FACTORY)
        self.user = User.objects.create_user(username='stock_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.warehouse = WareHouse.objects.create(
            merchant=self.merchant,
            name='库存仓',
            mode=WareHouseModeEnum.RESTRICT_IN,
        )
        category = ProductCategory.objects.create(
            merchant=self.merchant,
            name='库存品类',
            product_prefix='STK',
        )
        self.product = Product.objects.create(
            merchant=self.merchant,
            category=category,
            name='库存产品',
            human_id='STK-001',
            unit=ProductUnitEnum.METER,
        )

        self.stock_record, _, _ = stock_services.create_stock_change_record_with_details(
            merchant=self.merchant,
            created_by=None,
            type=stock_models.StockChangeTypeEnum.ADD,
            warehouse_id=self.warehouse.id,
            source_type=stock_models.StockChangeSourceEnum.PURCHASE,
            source_id=5001,
            products=[{'product': self.product.id, 'quantity': [Decimal('12')]}],
        )
        stock_services.make_stock_change_completed(self.stock_record)

    def test_offset_stock_change_success(self):
        response = self.client.post(
            f'/api/v1/stock-change/{self.stock_record.id}/offset/',
            {'reason': '数据修正'},
            format='json',
        )
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(response.data['status'], 'success')
        self.assertEqual(
            stock_models.StockSnapshot.objects.filter(
                stock_change_record=self.stock_record,
                cancelled=True,
            ).count(),
            1,
        )

    def test_offset_stock_change_duplicate(self):
        self.client.post(
            f'/api/v1/stock-change/{self.stock_record.id}/offset/',
            {'reason': '首次'},
            format='json',
        )
        response = self.client.post(
            f'/api/v1/stock-change/{self.stock_record.id}/offset/',
            {'reason': '再次'},
            format='json',
        )
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertIn('已执行红冲', response.data['error'])

class PaymentOrderAPITestCase(TestCase):
    def setUp(self):
        self.merchant = Merchant.objects.create(name='付款商户', type=MerchantTypeEnum.FACTORY)
        self.supplier = Supplier.objects.create(merchant=self.merchant, name='付款供应商')
        self.bank_account = BankAccount.objects.create(
            merchant=self.merchant,
            name='主账户',
            auto_number='BANK001',
        )
        self.user = User.objects.create_user(username='pay_user', password='pass123')
        self.employee = Employee.objects.create(
            merchant=self.merchant,
            sys_user=self.user,
            name='财务',
            status=EmployeeStatusEnum.ACTIVE,
        )
        self.client = APIClient()
        self.client.force_authenticate(user=self.user)
        self.payload = {
            'supplier': self.supplier.id,
            'bank_account': self.bank_account.id,
            'payment_date': '2025-11-26',
            'amount': '120.5',
            'discount_amount': '5.50',
            'remarks': '付款备注',
            'markup': '附言',
        }

    def test_create_payment_order_success(self):
        response = self.client.post('/api/v1/payment-orders/', self.payload, format='json')
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertIn('id', response.data)

    def test_create_payment_order_invalid_supplier(self):
        payload = {**self.payload, 'supplier': 999}
        response = self.client.post('/api/v1/payment-orders/', payload, format='json')
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

    def test_review_payment_order(self):
        resp = self.client.post('/api/v1/payment-orders/', self.payload, format='json')
        order_id = resp.data['id']
        response = self.client.post(f'/api/v1/payment-orders/{order_id}/review/', {'action': 'approve'}, format='json')
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data['status'], business_models.PaymentOrderStatusEnum.APPROVED)
        self.assertEqual(response.data['discount_amount'], '5.50')
        self.assertEqual(response.data['settlement_amount'], '126.00')
        response_cancel = self.client.post(
            f'/api/v1/payment-orders/{order_id}/review/',
            {'action': 'cancel'},
            format='json',
        )
        self.assertEqual(response_cancel.status_code, status.HTTP_400_BAD_REQUEST)

    def test_payment_order_requires_amount(self):
        payload = {**self.payload}
        payload.pop('amount')
        response = self.client.post('/api/v1/payment-orders/', payload, format='json')
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertIn('缺少 amount', response.data['error'])

    def test_payment_order_amount_must_be_positive(self):
        payload = {**self.payload, 'amount': '0'}
        response = self.client.post('/api/v1/payment-orders/', payload, format='json')
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertIn('amount 不能为 0', response.data['error'])

    def test_payment_order_invalid_bank_account(self):
        other_merchant = Merchant.objects.create(name='其他商户', type=MerchantTypeEnum.FACTORY)
        other_bank = BankAccount.objects.create(merchant=other_merchant, name='其他账户', auto_number='BANK999')
        payload = {**self.payload, 'bank_account': other_bank.id}
        response = self.client.post('/api/v1/payment-orders/', payload, format='json')
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertIn('银行账户不存在', response.data['error'])

    def test_payment_order_stores_bank_account_and_markup(self):
        response = self.client.post('/api/v1/payment-orders/', self.payload, format='json')
        order = business_models.PaymentOrder.objects.get(id=response.data['id'])
        self.assertEqual(order.bank_account_id, self.bank_account.id)
        self.assertEqual(order.markup, '附言')
        self.assertEqual(str(order.discount_amount), '5.50')
        self.assertEqual(str(order.settlement_amount), '126.00')

    def test_payment_order_discount_can_exceed_amount(self):
        payload = {**self.payload, 'discount_amount': '200.00'}
        response = self.client.post('/api/v1/payment-orders/', payload, format='json')
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        order = business_models.PaymentOrder.objects.get(id=response.data['id'])
        self.assertEqual(str(order.discount_amount), '200.00')
        self.assertEqual(str(order.settlement_amount), '320.50')

    def test_payment_order_discount_cannot_be_negative(self):
        payload = {**self.payload, 'discount_amount': '-1'}
        response = self.client.post('/api/v1/payment-orders/', payload, format='json')
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertIn('discount_amount 不能小于 0', response.data['error'])

class ReceiptOrderAPITestCase(TestCase):
    def setUp(self):
        self.merchant = Merchant.objects.create(name='收款商户', type=MerchantTypeEnum.FACTORY)
        self.customer = Customer.objects.create(
            merchant=self.merchant,
            name='客户C',
            mobile='13812345678',
            created_by=None,
        )
        self.bank_account = BankAccount.objects.create(
            merchant=self.merchant,
            name='收款账户',
            auto_number='BANK100',
        )
        self.user = User.objects.create_user(username='receipt_user', password='pass123')
        self.employee = Employee.objects.create(
            merchant=self.merchant,
            sys_user=self.user,
            name='财务员',
            status=EmployeeStatusEnum.ACTIVE,
        )
        self.client = APIClient()
        self.client.force_authenticate(user=self.user)
        self.payload = {
            'customer': self.customer.id,
            'bank_account': self.bank_account.id,
            'receipt_date': '2025-11-26',
            'amount': '88.00',
            'discount_amount': '3.00',
            'remarks': '收款备注',
            'markup': '收款附言',
        }

    def test_create_receipt_order_success(self):
        response = self.client.post('/api/v1/receipt-orders/', self.payload, format='json')
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertIn('id', response.data)

    def test_create_receipt_order_invalid_customer(self):
        payload = {**self.payload, 'customer': 999}
        response = self.client.post('/api/v1/receipt-orders/', payload, format='json')
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

    def test_review_receipt_order(self):
        resp = self.client.post('/api/v1/receipt-orders/', self.payload, format='json')
        order_id = resp.data['id']
        response = self.client.post(
            f'/api/v1/receipt-orders/{order_id}/review/',
            {'action': 'cancel'},
            format='json',
        )
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data['status'], business_models.ReceiptOrderStatusEnum.CANCELLED)
        self.assertEqual(response.data['discount_amount'], '3.00')
        self.assertEqual(response.data['settlement_amount'], '91.00')

    def test_receipt_order_requires_amount(self):
        payload = {**self.payload}
        payload.pop('amount')
        response = self.client.post('/api/v1/receipt-orders/', payload, format='json')
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertIn('缺少 amount', response.data['error'])

    def test_receipt_order_amount_must_be_positive(self):
        payload = {**self.payload, 'amount': '0'}
        response = self.client.post('/api/v1/receipt-orders/', payload, format='json')
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertIn('amount 不能为 0', response.data['error'])

    def test_receipt_order_stores_bank_account_and_markup(self):
        response = self.client.post('/api/v1/receipt-orders/', self.payload, format='json')
        order = business_models.ReceiptOrder.objects.get(id=response.data['id'])
        self.assertEqual(order.bank_account_id, self.bank_account.id)
        self.assertEqual(order.markup, '收款附言')
        self.assertEqual(str(order.discount_amount), '3.00')
        self.assertEqual(str(order.settlement_amount), '91.00')

    def test_receipt_order_discount_can_exceed_amount(self):
        payload = {**self.payload, 'discount_amount': '150.00'}
        response = self.client.post('/api/v1/receipt-orders/', payload, format='json')
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        order = business_models.ReceiptOrder.objects.get(id=response.data['id'])
        self.assertEqual(str(order.settlement_amount), '238.00')

    def test_receipt_order_discount_cannot_be_negative(self):
        payload = {**self.payload, 'discount_amount': '-1'}
        response = self.client.post('/api/v1/receipt-orders/', payload, format='json')
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertIn('discount_amount 不能小于 0', response.data['error'])

    def test_receipt_order_invalid_bank_account(self):
        other_merchant = Merchant.objects.create(name='其他收款商户', type=MerchantTypeEnum.FACTORY)
        other_bank = BankAccount.objects.create(merchant=other_merchant, name='其他收款账户', auto_number='BANK888')
        payload = {**self.payload, 'bank_account': other_bank.id}
        response = self.client.post('/api/v1/receipt-orders/', payload, format='json')
        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)
        self.assertEqual(
            business_models.BalanceChangeRecord.objects.filter(
                source_type=business_models.BalanceChangeSourceEnum.PURCHASE_ORDER,
                source_id=order.id,
                red_flush_id=order.red_flush_id,
            ).count(),
            2,
        )
        self.assertTrue(
            stock_models.StockChangeRecord.objects.filter(
                source_type=stock_models.StockChangeSourceEnum.OFFSET,
                red_flush_id=order.red_flush_id,
            ).exists()
        )

    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_sales_order_red_flush_without_stock_success(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,
        )

        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'])
        order.refresh_from_db()
        self.assertTrue(order.is_red_flushed)
        self.assertEqual(
            business_models.BalanceChangeRecord.objects.filter(
                source_type=business_models.BalanceChangeSourceEnum.SALES_ORDER,
                source_id=order.id,
                red_flush_id=order.red_flush_id,
            ).count(),
            2,
        )
        self.assertFalse(
            stock_models.StockChangeRecord.objects.filter(red_flush_id=order.red_flush_id).exists()
        )

    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_requires_employee_permission(self):
        order = self._create_approved_payment_order()
        user_without_employee = User.objects.create_user(username='red_flush_no_employee', password='pass123')
        self.client.force_authenticate(user=user_without_employee)

        response = self.client.post(
            f'/api/v1/payment-orders/{order.id}/red-flush/',
            {'reason': '无员工身份红冲'},
            format='json',
        )

        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
        self.assertEqual(response.data['error'], '无权限访问')
        order.refresh_from_db()
        self.assertFalse(order.is_red_flushed)

    def test_purchase_order_red_flush_stock_failure_returns_400_and_rolls_back(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,
        )

        response = self.client.post(
            f'/api/v1/purchase-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)
        records = business_models.BalanceChangeRecord.objects.filter(
            source_type=business_models.BalanceChangeSourceEnum.PURCHASE_ORDER,
            source_id=order.id,
        )
        self.assertEqual(records.count(), 1)
        self.assertFalse(records.first().cancelled)

    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)
        self.customer = Customer.objects.create(
            merchant=self.merchant,
            name='余额客户',
            mobile='13800000000',
            created_by=None,
        )
        self.user = User.objects.create_user(username='balance_user', password='pass123')
        self.employee = Employee.objects.create(
            merchant=self.merchant,
            sys_user=self.user,
            name='财务查询',
            status=EmployeeStatusEnum.ACTIVE,
        )
        self.client = APIClient()
        self.client.force_authenticate(user=self.user)

    def test_customer_balance_defaults_to_zero(self):
        response = self.client.get(f'/api/v1/customers/{self.customer.id}/balance/')
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data['balance'], '0')

    def test_customer_balance_reflects_sales_and_receipts(self):
        # 创建销售单
        product_category = ProductCategory.objects.create(
            merchant=self.merchant,
            name='余额品类',
            product_prefix='BAL',
        )
        product = Product.objects.create(
            merchant=self.merchant,
            category=product_category,
            name='余额产品',
            human_id='BAL-001',
            unit=ProductUnitEnum.METER,
        )
        warehouse = WareHouse.objects.create(
            merchant=self.merchant,
            name='余额仓',
            mode=WareHouseModeEnum.RESTRICT_IN,
        )
        order = services.create_sales_order(
            merchant=self.merchant,
            customer=self.customer,
            order_date='2025-11-26',
            warehouse=warehouse,
            operator=self.employee,
            items=[{'product_id': product.id, 'numbers': [5], 'price': '10', 'unit': '米'}],
        )
        services.review_sales_order(
            sales_order=order,
            target_status=business_models.SalesOrderStatusEnum.APPROVED,
            reviewed_by=self.user,
        )
        response = self.client.get(f'/api/v1/customers/{self.customer.id}/balance/')
        self.assertEqual(response.data['balance'], '50.00')

        receipt = services.create_receipt_order(
            merchant=self.merchant,
            customer=self.customer,
            receipt_date='2025-11-27',
            amount='20',
            operator=self.employee,
        )
        services.review_receipt_order(
            receipt_order=receipt,
            target_status=business_models.ReceiptOrderStatusEnum.APPROVED,
            reviewed_by=self.user,
        )
        response = self.client.get(f'/api/v1/customers/{self.customer.id}/balance/')
        self.assertEqual(response.data['balance'], '30.00')

        business_models.ExternalCustomerStatementOrder.objects.create(
            merchant=self.merchant,
            customer=self.customer,
            category=business_models.ExternalCustomerStatementCategoryEnum.SALE,
            external_source_id='XS-BAL-001',
            occurred_at=datetime.date(2025, 11, 28),
            total_amount=Decimal('100.00'),
            zk_amount=Decimal('10.00'),
            sf_amount=Decimal('20.00'),
        )
        business_models.ExternalCustomerStatementOrder.objects.create(
            merchant=self.merchant,
            customer=self.customer,
            category=business_models.ExternalCustomerStatementCategoryEnum.SALE_RETURN,
            external_source_id='XT-BAL-001',
            occurred_at=datetime.date(2025, 11, 29),
            total_amount=Decimal('15.00'),
            zk_amount=Decimal('5.00'),
        )

        response = self.client.get(f'/api/v1/customers/{self.customer.id}/balance/')
        self.assertEqual(response.data['balance'], '80.00')

        statement_payload = services.build_customer_statement(merchant=self.merchant, customer=self.customer)
        self.assertEqual(statement_payload['records'][0]['current_balance'], response.data['balance'])


class SupplierBalanceAPITestCase(TestCase):
    def setUp(self):
        self.merchant = Merchant.objects.create(name='供应商余额商户', type=MerchantTypeEnum.FACTORY)
        self.supplier = Supplier.objects.create(merchant=self.merchant, name='供应商余额')
        self.user = User.objects.create_user(username='supplier-balance', password='pass123')
        self.employee = Employee.objects.create(
            merchant=self.merchant,
            sys_user=self.user,
            name='供应商财务',
            status=EmployeeStatusEnum.ACTIVE,
        )
        self.client = APIClient()
        self.client.force_authenticate(user=self.user)

    def test_supplier_balance_api(self):
        response = self.client.get(f'/api/v1/suppliers/{self.supplier.id}/balance/')
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data['supplier'], self.supplier.id)


class StatementRecordAPITestCase(TestCase):
    """对账单单条记录查询 API 测试"""

    def setUp(self):
        self.merchant = Merchant.objects.create(name='对账商户', type=MerchantTypeEnum.FACTORY)
        self.customer = Customer.objects.create(
            merchant=self.merchant,
            name='对账客户',
            mobile='13800000000',
            created_by=None,
        )
        self.supplier = Supplier.objects.create(merchant=self.merchant, name='对账供应商')
        category = ProductCategory.objects.create(
            merchant=self.merchant,
            name='对账品类',
            product_prefix='STM',
        )
        self.product = Product.objects.create(
            merchant=self.merchant,
            category=category,
            name='对账产品',
            human_id='STM-001',
            unit=ProductUnitEnum.METER,
        )
        self.warehouse = WareHouse.objects.create(
            merchant=self.merchant,
            name='对账仓库',
            mode=WareHouseModeEnum.UNRESTRICTED,
        )
        self.user = User.objects.create_user(username='statement_user', password='pass123')
        self.employee = Employee.objects.create(
            merchant=self.merchant,
            sys_user=self.user,
            name='财务',
            status=EmployeeStatusEnum.ACTIVE,
        )
        self.client = APIClient()
        self.client.force_authenticate(user=self.user)

        self.sales_order = business_models.SalesOrder.objects.create(
            merchant=self.merchant,
            customer=self.customer,
            sales_date='2025-11-20',
            operator=self.employee,
            warehouse=self.warehouse,
            status=business_models.SalesOrderStatusEnum.APPROVED,
        )
        business_models.SalesOrderItem.objects.create(
            sales_order=self.sales_order,
            product=self.product,
            price=Decimal('12.50'),
            color='红',
            quantity=Decimal('5'),
            unit='米',
            empty_diff_percent=Decimal('0'),
            quantity_of_rolls='2,3',
            num_of_rolls=2,
            spec='32S',
        )

        self.purchase_order = business_models.PurchaseOrder.objects.create(
            merchant=self.merchant,
            supplier=self.supplier,
            purchase_date='2025-11-21',
            operator=self.employee,
            warehouse=self.warehouse,
            status=business_models.PurchaseOrderStatusEnum.APPROVED,
        )
        business_models.PurchaseOrderItem.objects.create(
            purchase_order=self.purchase_order,
            product=self.product,
            price=Decimal('8.30'),
            color='蓝',
            quantity=Decimal('10'),
            unit='米',
            empty_diff_percent=Decimal('0'),
            quantity_of_rolls='6,4',
            num_of_rolls=2,
            spec='40S',
        )

    def test_get_customer_sales_order_record(self):
        response = self.client.get(
            '/api/v1/statements/record/',
            {
                'counterparty_type': 'customer',
                'counterparty_id': self.customer.id,
                'order_type': 'sales_order',
                'order_id': self.sales_order.id,
            },
        )
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data['counterparty'], self.customer.id)
        self.assertEqual(response.data['counterparty_name'], self.customer.name)
        self.assertEqual(len(response.data['records']), 1)
        record = response.data['records'][0]
        self.assertEqual(record['source_type'], 'sales_order')
        self.assertEqual(record['source_id'], self.sales_order.id)
        self.assertEqual(record['counterparty'], self.customer.id)
        self.assertIsNotNone(record.get('warehouse'))
        self.assertEqual(record['warehouse']['id'], self.warehouse.id)
        self.assertEqual(record['warehouse']['name'], self.warehouse.name)
        self.assertTrue(record['items'])
        self.assertEqual(record['items'][0]['color'], '红')
        self.assertEqual(record['items'][0]['spec'], '32S')
        self.assertEqual(record['items'][0]['quantity_of_rolls'], [2, 3])
        self.assertEqual(record['items'][0]['num_of_rolls'], 2)

    def test_get_supplier_purchase_order_record(self):
        response = self.client.get(
            '/api/v1/statements/record/',
            {
                'counterparty_type': 'supplier',
                'counterparty_id': self.supplier.id,
                'order_type': 'purchase_order',
                'order_id': self.purchase_order.id,
            },
        )
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        record = response.data['records'][0]
        self.assertEqual(record['source_type'], 'purchase_order')
        self.assertEqual(record['source_id'], self.purchase_order.id)
        self.assertEqual(record['counterparty'], self.supplier.id)
        self.assertIsNotNone(record.get('warehouse'))
        self.assertEqual(record['warehouse']['id'], self.warehouse.id)
        self.assertEqual(record['warehouse']['name'], self.warehouse.name)
        self.assertTrue(record['items'])
        self.assertEqual(record['items'][0]['color'], '蓝')
        self.assertEqual(record['items'][0]['spec'], '40S')
        self.assertEqual(record['items'][0]['quantity_of_rolls'], [6, 4])
        self.assertEqual(record['items'][0]['num_of_rolls'], 2)

    def test_statement_record_not_found(self):
        response = self.client.get(
            '/api/v1/statements/record/',
            {
                'counterparty_type': 'customer',
                'counterparty_id': self.customer.id,
                'order_type': 'sales_order',
                'order_id': 9999,
            },
        )
        self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
        self.assertIn('未找到匹配的对账记录', response.data['error'])

    def test_invalid_order_type_returns_400(self):
        response = self.client.get(
            '/api/v1/statements/record/',
            {
                'counterparty_type': 'customer',
                'counterparty_id': self.customer.id,
                'order_type': 'unknown',
                'order_id': self.sales_order.id,
            },
        )
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertIn('order_type', response.data)


@override_settings(
    CELERY_TASK_ALWAYS_EAGER=True,
    CELERY_TASK_EAGER_PROPAGATES=True,
)
class CeleryTasksTestCase(TestCase):
    """验证 Celery 任务的执行结果"""

    def setUp(self):
        self.merchant = Merchant.objects.create(name='Celery商户', type=MerchantTypeEnum.FACTORY)
        category = ProductCategory.objects.create(
            merchant=self.merchant,
            name='Celery品类',
            product_prefix='CLY',
        )
        for idx in range(3):
            Product.objects.create(
                merchant=self.merchant,
                category=category,
                name=f'Celery产品{idx}',
                human_id=f'CLY-{idx:03d}',
                unit=ProductUnitEnum.METER,
            )

    def test_ping_task_returns_payload(self):
        # 已弃用的演示任务，测试移除
        self.skipTest('deprecated demo task')

    def test_merchant_product_count(self):
        # 已弃用的演示任务，测试移除
        self.skipTest('deprecated demo task')

    def test_backup_database_creates_file(self):
        tmpdir = Path(tempfile.mkdtemp())
        self.addCleanup(lambda: shutil.rmtree(tmpdir, ignore_errors=True))
        with patch('api_v1.tasks._dump_database_to_sql') as mock_dump:
            def fake_dump(path: Path):
                path.write_text('-- dummy sql\n', encoding='utf-8')

            mock_dump.side_effect = fake_dump
            result = tasks.backup_database.delay(output_dir=str(tmpdir), filename_prefix='test-backup')
        payload = result.get(timeout=10)
        backup_path = Path(payload['backup_path'])
        self.assertTrue(backup_path.exists())
        self.assertTrue(backup_path.is_file())
        self.assertEqual(backup_path.parent.resolve(), tmpdir.resolve())
        self.assertEqual(backup_path.suffix, '.sql')
        self.assertTrue(backup_path.read_text(encoding='utf-8').strip())


class PrintingJobWorkStateAPITestCase(TestCase):
    """印染任务 work_state 字段 API 覆盖"""

    def setUp(self):
        self.merchant = Merchant.objects.create(
            name='印染工厂',
            type=MerchantTypeEnum.FACTORY,
        )
        self.user = User.objects.create_user(username='factory_user', password='pass123')
        self.employee = Employee.objects.create(
            merchant=self.merchant,
            sys_user=self.user,
            name='印染员',
        )
        perms = Permission.objects.filter(
            codename__in=['add_printingjob', 'view_printingjob', 'change_printingjob']
        )
        self.user.user_permissions.set(perms)
        self.client = APIClient()
        self.client.force_authenticate(user=self.user)

        category = ProductCategory.objects.create(
            merchant=self.merchant,
            name='面料',
            product_prefix='FAB',
        )
        self.product = Product.objects.create(
            merchant=self.merchant,
            category=category,
            name='棉布',
            human_id='FAB-100',
            unit=ProductUnitEnum.METER,
        )
        self.customer = Customer.objects.create(
            merchant=self.merchant,
            name='客户A',
        )
        self.printing_order = printing_models.PrintingOrder.objects.create(
            customer=self.customer,
            fabric='纯棉',
            width='150cm',
        )

    def test_create_job_with_work_state(self):
        payload = {
            'printing_order': self.printing_order.id,
            'product': self.product.id,
            'quantity': 10,
            'unit': '米',
            'work_state': printing_models.PrintingJobWorkStateEnum.WAITING_FOR_DELIVERY,
        }
        resp = self.client.post('/api/v1/printing-jobs/', payload, format='json')
        self.assertEqual(resp.status_code, status.HTTP_201_CREATED)
        self.assertEqual(
            resp.data['work_state'],
            printing_models.PrintingJobWorkStateEnum.WAITING_FOR_DELIVERY,
        )

        job_id = resp.data['id']
        detail = self.client.get(f'/api/v1/printing-jobs/{job_id}/')
        # 客户可见性过滤：customer 未绑定 created_by/visible_employees 时，任务对该员工不可见 => 404
        self.assertEqual(detail.status_code, status.HTTP_404_NOT_FOUND)

        # 将客户显式授权给当前 employee 后应可见 => 200
        self.customer.created_by = self.employee
        self.customer.save(update_fields=['created_by'])
        detail = self.client.get(f'/api/v1/printing-jobs/{job_id}/')
        self.assertEqual(detail.status_code, status.HTTP_200_OK)
        self.assertEqual(detail.data['work_state_display'], '待送货')

    def test_update_job_work_state(self):
        job = printing_models.PrintingJob.objects.create(
            printing_order=self.printing_order,
            product=self.product,
            quantity=5,
            unit='米',
        )
        url = f'/api/v1/printing-jobs/{job.id}/'
        patch_resp = self.client.patch(
            url,
            {'work_state': printing_models.PrintingJobWorkStateEnum.FINISHED},
            format='json',
        )
        # merchant 隔离：job.merchant 为空时，不属于当前员工商户 => 404
        self.assertEqual(patch_resp.status_code, status.HTTP_404_NOT_FOUND)

        # 创建一个显式属于当前商户的 job，并让 customer 对 employee 可见后应可更新/可查询
        self.customer.created_by = self.employee
        self.customer.save(update_fields=['created_by'])
        visible_job = printing_models.PrintingJob.objects.create(
            merchant=self.merchant,
            printing_order=self.printing_order,
            product=self.product,
            quantity=5,
            unit='米',
        )
        visible_url = f'/api/v1/printing-jobs/{visible_job.id}/'
        patch_resp = self.client.patch(
            visible_url,
            {'work_state': printing_models.PrintingJobWorkStateEnum.FINISHED},
            format='json',
        )
        self.assertEqual(patch_resp.status_code, status.HTTP_200_OK)
        visible_job.refresh_from_db()
        self.assertEqual(visible_job.work_state, printing_models.PrintingJobWorkStateEnum.FINISHED)
        detail = self.client.get(visible_url)
        self.assertEqual(detail.status_code, status.HTTP_200_OK)
        self.assertEqual(detail.data['work_state_display'], '已完结')


class PrintingJobBilledQuantityTestCase(TestCase):
    """验证印染任务的开单数量汇总"""

    def setUp(self):
        self.merchant = Merchant.objects.create(
            name='印染工厂B',
            type=MerchantTypeEnum.FACTORY,
        )
        self.user = User.objects.create_user(username='factory_user_b', password='pass123')
        self.employee = Employee.objects.create(
            merchant=self.merchant,
            sys_user=self.user,
            name='操作员B',
        )
        category = ProductCategory.objects.create(
            merchant=self.merchant,
            name='面料B',
            product_prefix='FAB',
        )
        self.product = Product.objects.create(
            merchant=self.merchant,
            category=category,
            name='棉布B',
            human_id='FAB-200',
            unit=ProductUnitEnum.METER,
        )
        self.customer = Customer.objects.create(
            merchant=self.merchant,
            name='客户B',
        )
        self.warehouse = WareHouse.objects.create(
            merchant=self.merchant,
            name='仓库B',
            mode=WareHouseModeEnum.UNRESTRICTED,
        )
        self.printing_order = printing_models.PrintingOrder.objects.create(
            customer=self.customer,
            fabric='棉布',
            width='150cm',
        )
        self.job = printing_models.PrintingJob.objects.create(
            printing_order=self.printing_order,
            product=self.product,
            quantity=100,
            unit='米',
        )
        self.sales_order = business_models.SalesOrder.objects.create(
            merchant=self.merchant,
            customer=self.customer,
            sales_date=datetime.date.today(),
            operator=self.employee,
            warehouse=self.warehouse,
        )

    def test_billed_quantity_empty(self):
        self.assertEqual(self.job.billed_quantity, Decimal('0'))

    def test_billed_quantity_sum(self):
        business_models.SalesOrderItem.objects.create(
            sales_order=self.sales_order,
            product=self.product,
            price=Decimal('10'),
            quantity=Decimal('12.5'),
            unit='米',
            empty_diff_percent=Decimal('0'),
            num_of_rolls=1,
            printing_job=self.job,
        )
        business_models.SalesOrderItem.objects.create(
            sales_order=self.sales_order,
            product=self.product,
            price=Decimal('11'),
            quantity=Decimal('7.5'),
            unit='米',
            empty_diff_percent=Decimal('0'),
            num_of_rolls=1,
            printing_job=self.job,
        )
        self.assertEqual(self.job.billed_quantity, Decimal('20.0'))
