baseline: 批次A-D 成果 + membership 半成品(测试红)
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
"""sales 集成测试:销售单过账 → 扣库存 + 生成应收。"""
|
||||
|
||||
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 import services as inv_services
|
||||
from apps.inventory.models import Stock
|
||||
from apps.finance.models import Receivable
|
||||
from apps.sales import services
|
||||
from apps.sales.models import SalesBill
|
||||
|
||||
|
||||
@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 customer(tenant):
|
||||
from apps.partner.models import Customer
|
||||
return baker.make(Customer, tenant=tenant, code="C001", 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_sales_bill_calculates_total(tenant, customer, warehouse, product):
|
||||
bill = services.create_sales_bill(
|
||||
tenant=tenant, customer=customer, warehouse=warehouse,
|
||||
lines=[
|
||||
{"product": product, "quantity": Decimal("5"), "unit_price": Decimal("12")},
|
||||
{"product": product, "quantity": Decimal("3"), "unit_price": Decimal("12")},
|
||||
],
|
||||
)
|
||||
assert bill.total_amount == Decimal("96")
|
||||
assert bill.state == "draft"
|
||||
assert bill.lines.count() == 2
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_confirm_sales_bill_deducts_stock_and_creates_receivable(
|
||||
tenant, customer, warehouse, product
|
||||
):
|
||||
# 准备库存 20
|
||||
inv_services.inbound(
|
||||
tenant=tenant, warehouse=warehouse, product=product,
|
||||
quantity=Decimal("20"), unit_cost=Decimal("8"),
|
||||
)
|
||||
# 销售 7
|
||||
bill = services.create_sales_bill(
|
||||
tenant=tenant, customer=customer, warehouse=warehouse,
|
||||
lines=[{"product": product, "quantity": Decimal("7"), "unit_price": Decimal("12")}],
|
||||
)
|
||||
services.confirm_sales_bill(bill)
|
||||
|
||||
# 库存
|
||||
stock = Stock.objects.get(tenant=tenant, warehouse=warehouse, product=product)
|
||||
assert stock.on_hand == Decimal("13")
|
||||
|
||||
# 应收
|
||||
recv = Receivable.objects.get(tenant=tenant, source_ref=bill.bill_no)
|
||||
assert recv.total_amount == Decimal("84")
|
||||
assert recv.status == "open"
|
||||
|
||||
# 单据状态
|
||||
bill.refresh_from_db()
|
||||
assert bill.state == "confirmed"
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_confirm_insufficient_stock_rolls_back(tenant, customer, warehouse, product):
|
||||
inv_services.inbound(
|
||||
tenant=tenant, warehouse=warehouse, product=product,
|
||||
quantity=Decimal("5"),
|
||||
)
|
||||
bill = services.create_sales_bill(
|
||||
tenant=tenant, customer=customer, warehouse=warehouse,
|
||||
lines=[{"product": product, "quantity": Decimal("10"), "unit_price": Decimal("12")}],
|
||||
)
|
||||
with pytest.raises(inv_services.InsufficientStock):
|
||||
services.confirm_sales_bill(bill)
|
||||
|
||||
# 库存不变(事务回滚)
|
||||
stock = Stock.objects.get(tenant=tenant, warehouse=warehouse, product=product)
|
||||
assert stock.on_hand == Decimal("5")
|
||||
|
||||
# 应收不生成
|
||||
assert Receivable.objects.filter(tenant=tenant, source_ref=bill.bill_no).count() == 0
|
||||
|
||||
# 单据仍是 draft
|
||||
bill.refresh_from_db()
|
||||
assert bill.state == "draft"
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_confirm_twice_raises(tenant, customer, warehouse, product):
|
||||
inv_services.inbound(
|
||||
tenant=tenant, warehouse=warehouse, product=product,
|
||||
quantity=Decimal("10"),
|
||||
)
|
||||
bill = services.create_sales_bill(
|
||||
tenant=tenant, customer=customer, warehouse=warehouse,
|
||||
lines=[{"product": product, "quantity": Decimal("3"), "unit_price": Decimal("10")}],
|
||||
)
|
||||
services.confirm_sales_bill(bill)
|
||||
with pytest.raises(ValueError):
|
||||
services.confirm_sales_bill(bill)
|
||||
|
||||
|
||||
# --- API 集成测试 ---
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_api_list_sales_bills(auth_client, tenant, customer, warehouse):
|
||||
baker.make(
|
||||
SalesBill, tenant=tenant, customer=customer, warehouse=warehouse,
|
||||
bill_no="XS0001", total_amount=Decimal("100"),
|
||||
)
|
||||
resp = auth_client.get("/api/v1/sales/bills/")
|
||||
assert resp.status_code == 200
|
||||
assert resp.json()["count"] == 1
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_api_list_sales_orders(auth_client, tenant, customer, warehouse):
|
||||
from apps.sales.models import SalesOrder
|
||||
baker.make(
|
||||
SalesOrder, tenant=tenant, customer=customer, warehouse=warehouse,
|
||||
bill_no="SO0001", total_amount=Decimal("0"),
|
||||
)
|
||||
resp = auth_client.get("/api/v1/sales/orders/")
|
||||
assert resp.status_code == 200
|
||||
assert resp.json()["count"] == 1
|
||||
Reference in New Issue
Block a user