126 lines
3.9 KiB
Python
126 lines
3.9 KiB
Python
"""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
|