"""purchase 集成测试:进货单过账 → 写库存 + 生成应付。""" import pytest from decimal import Decimal from model_bakery import baker from rest_framework.test import APIClient from rest_framework_simplejwt.tokens import RefreshToken from apps.inventory.models import Stock, StockMovement from apps.finance.models import Payable from apps.purchase import services from apps.purchase.models import PurchaseBill @pytest.fixture def auth_client(db, user, tenant): c = APIClient() refresh = RefreshToken.for_user(user) c.credentials( HTTP_AUTHORIZATION=f"Bearer {refresh.access_token}", HTTP_X_TENANT_ID=tenant.code, ) return c @pytest.fixture def supplier(tenant): from apps.partner.models import Supplier return baker.make(Supplier, tenant=tenant, code="S001", name="供应商A") @pytest.fixture def warehouse(tenant): from apps.inventory.models import Warehouse return baker.make(Warehouse, tenant=tenant, code="WH01", name="主仓") @pytest.fixture def product(tenant): from apps.catalog.models import Product return baker.make(Product, tenant=tenant, code="P001", name="商品A") # --- services 单元测试 --- @pytest.mark.django_db def test_create_purchase_bill_calculates_total(tenant, supplier, warehouse, product): bill = services.create_purchase_bill( tenant=tenant, supplier=supplier, warehouse=warehouse, lines=[ {"product": product, "quantity": Decimal("10"), "unit_price": Decimal("5")}, {"product": product, "quantity": Decimal("3"), "unit_price": Decimal("5")}, ], ) assert bill.total_amount == Decimal("65") assert bill.state == "draft" assert bill.lines.count() == 2 @pytest.mark.django_db def test_confirm_purchase_bill_writes_stock_and_payable(tenant, supplier, warehouse, product): bill = services.create_purchase_bill( tenant=tenant, supplier=supplier, warehouse=warehouse, lines=[ {"product": product, "quantity": Decimal("20"), "unit_price": Decimal("8")}, ], ) services.confirm_purchase_bill(bill) # 库存 stock = Stock.objects.get(tenant=tenant, warehouse=warehouse, product=product) assert stock.on_hand == Decimal("20") assert stock.avg_cost == Decimal("8") # 流水 movements = StockMovement.objects.filter(tenant=tenant) assert movements.count() == 1 assert movements.first().source_type == "purchase" # 应付 payable = Payable.objects.get(tenant=tenant, source_ref=bill.bill_no) assert payable.total_amount == Decimal("160") assert payable.status == "open" # 单据状态 bill.refresh_from_db() assert bill.state == "confirmed" @pytest.mark.django_db def test_confirm_twice_raises(tenant, supplier, warehouse, product): bill = services.create_purchase_bill( tenant=tenant, supplier=supplier, warehouse=warehouse, lines=[{"product": product, "quantity": Decimal("5"), "unit_price": Decimal("10")}], ) services.confirm_purchase_bill(bill) with pytest.raises(ValueError): services.confirm_purchase_bill(bill) # --- API 集成测试 --- @pytest.mark.django_db def test_api_list_purchase_bills(auth_client, tenant, supplier, warehouse, product): baker.make( PurchaseBill, tenant=tenant, supplier=supplier, warehouse=warehouse, bill_no="PB0001", total_amount=Decimal("100"), ) resp = auth_client.get("/api/v1/purchase/bills/") assert resp.status_code == 200 assert resp.json()["count"] == 1 @pytest.mark.django_db def test_api_list_purchase_orders(auth_client, tenant, supplier, warehouse): from apps.purchase.models import PurchaseOrder baker.make( PurchaseOrder, tenant=tenant, supplier=supplier, warehouse=warehouse, bill_no="PO0001", total_amount=Decimal("0"), ) resp = auth_client.get("/api/v1/purchase/orders/") assert resp.status_code == 200 assert resp.json()["count"] == 1