133 lines
4.2 KiB
Python
133 lines
4.2 KiB
Python
"""全渠道与电商对接集成测试。"""
|
|
|
|
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.catalog.models import Product
|
|
from apps.inventory.models import Warehouse
|
|
from apps.partner.models import Customer
|
|
from apps.channel.models import ChannelAccount, ChannelOrder, WebhookEvent
|
|
from apps.channel import services as channel_services
|
|
|
|
|
|
@pytest.fixture
|
|
def auth_client(db, user, tenant):
|
|
client = APIClient()
|
|
refresh = RefreshToken.for_user(user)
|
|
client.credentials(
|
|
HTTP_AUTHORIZATION=f"Bearer {refresh.access_token}",
|
|
HTTP_X_TENANT_ID=tenant.code,
|
|
)
|
|
return client
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_sync_and_convert_channel_order(tenant):
|
|
wh = baker.make(Warehouse, tenant=tenant, code="WH_ONLINE", name="电商仓")
|
|
cust = baker.make(Customer, tenant=tenant, code="C_ONLINE", name="抖音买家总客户")
|
|
prod = baker.make(Product, tenant=tenant, code="P_DOUYIN", name="抖音热销饮品", sale_price=Decimal("45.00"))
|
|
|
|
account = ChannelAccount.objects.create(
|
|
tenant=tenant,
|
|
platform="douyin",
|
|
shop_id="DY88888",
|
|
shop_name="旗舰店",
|
|
default_warehouse=wh,
|
|
default_customer=cust,
|
|
)
|
|
|
|
# 模拟外部订单
|
|
raw_payload = {
|
|
"external_order_id": "DY-ORD-10001",
|
|
"order_status": "PAID",
|
|
"order_amount": "90.00",
|
|
"buyer_name": "李四",
|
|
"buyer_phone": "13911112222",
|
|
"receiver_address": "北京市海淀区",
|
|
"items": [
|
|
{"product_code": "P_DOUYIN", "quantity": "2", "unit_price": "45.00"}
|
|
],
|
|
}
|
|
|
|
corder = ChannelOrder.objects.create(
|
|
tenant=tenant,
|
|
channel_account=account,
|
|
external_order_id="DY-ORD-10001",
|
|
order_status="PAID",
|
|
order_amount=Decimal("90.00"),
|
|
buyer_name="李四",
|
|
buyer_phone="13911112222",
|
|
raw_payload=raw_payload,
|
|
sync_status="pending",
|
|
)
|
|
|
|
# 转单为内部销售单
|
|
sales_order = channel_services.convert_channel_order_to_sales_order(corder)
|
|
assert sales_order is not None
|
|
assert sales_order.state == "confirmed"
|
|
assert sales_order.total_amount == Decimal("90.00")
|
|
assert sales_order.lines.count() == 1
|
|
assert sales_order.customer == cust
|
|
|
|
corder.refresh_from_db()
|
|
assert corder.sync_status == "converted"
|
|
assert corder.sales_order == sales_order
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_channel_inbound_webhook(tenant):
|
|
wh = baker.make(Warehouse, tenant=tenant, code="WH_MALL")
|
|
cust = baker.make(Customer, tenant=tenant, code="C_MALL")
|
|
prod = baker.make(Product, tenant=tenant, code="P_HOT", sale_price=Decimal("100.00"))
|
|
|
|
account = ChannelAccount.objects.create(
|
|
tenant=tenant,
|
|
platform="douyin",
|
|
shop_id="SHOP_WB_01",
|
|
shop_name="抖音小店A",
|
|
default_warehouse=wh,
|
|
default_customer=cust,
|
|
)
|
|
|
|
payload = {
|
|
"shop_id": "SHOP_WB_01",
|
|
"external_order_id": "WB-DY-9999",
|
|
"order_amount": "200.00",
|
|
"buyer_name": "王五",
|
|
"items": [
|
|
{"product_code": "P_HOT", "quantity": "2", "unit_price": "100.00"}
|
|
],
|
|
}
|
|
|
|
event = channel_services.handle_inbound_webhook(
|
|
tenant=tenant,
|
|
platform="douyin",
|
|
event_type="order.paid",
|
|
payload=payload,
|
|
)
|
|
assert event.status == "processed"
|
|
assert ChannelOrder.objects.filter(tenant=tenant, external_order_id="WB-DY-9999").exists()
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_channel_api_endpoints(auth_client, tenant):
|
|
# 1. 创建店铺
|
|
payload = {
|
|
"platform": "douyin",
|
|
"shop_id": "DY_TEST_01",
|
|
"shop_name": "测试抖店",
|
|
"app_key": "key_123",
|
|
}
|
|
r = auth_client.post("/api/v1/channel/accounts/", payload, format="json")
|
|
assert r.status_code == 201
|
|
acc_id = r.json()["id"]
|
|
|
|
# 2. 触发同步订单 action
|
|
baker.make(Product, tenant=tenant, code="P001", sale_price=Decimal("10"))
|
|
r2 = auth_client.post(f"/api/v1/channel/accounts/{acc_id}/sync-orders/", {"auto_convert": True}, format="json")
|
|
assert r2.status_code == 200
|
|
assert r2.json()["ok"] is True
|