140 lines
4.6 KiB
Python
140 lines
4.6 KiB
Python
from decimal import Decimal
|
|
|
|
import pytest
|
|
from django.urls import reverse
|
|
from django.utils import timezone
|
|
from rest_framework import status
|
|
|
|
from commerce.models import Cost, Product, Sale, SaleLine, SourceSystem, StockSnapshot
|
|
|
|
pytestmark = pytest.mark.django_db
|
|
|
|
|
|
def test_commerce_api_requires_authentication(api_client):
|
|
response = api_client.get(reverse("api:v1:commerce:product-list"))
|
|
assert response.status_code == status.HTTP_401_UNAUTHORIZED
|
|
|
|
|
|
def test_employee_can_read_but_cannot_create_product(api_client, user_factory):
|
|
user = user_factory()
|
|
Product.objects.create(sku="FISHKA", name="Фишка")
|
|
api_client.force_authenticate(user=user)
|
|
|
|
list_response = api_client.get(reverse("api:v1:commerce:product-list"))
|
|
create_response = api_client.post(
|
|
reverse("api:v1:commerce:product-list"),
|
|
{"sku": "NEW", "name": "Новый товар", "kind": "product"},
|
|
format="json",
|
|
)
|
|
|
|
assert list_response.status_code == status.HTTP_200_OK
|
|
assert list_response.data["count"] == 1
|
|
assert create_response.status_code == status.HTTP_403_FORBIDDEN
|
|
|
|
|
|
def test_staff_can_create_product(api_client, user_factory):
|
|
user = user_factory(staff=True)
|
|
api_client.force_authenticate(user=user)
|
|
|
|
response = api_client.post(
|
|
reverse("api:v1:commerce:product-list"),
|
|
{"sku": "NEW", "name": "Новый товар", "kind": "product"},
|
|
format="json",
|
|
)
|
|
|
|
assert response.status_code == status.HTTP_201_CREATED
|
|
assert Product.objects.filter(sku="NEW").exists()
|
|
|
|
|
|
def test_staff_can_manage_normalized_commerce_facts(api_client, user_factory):
|
|
user = user_factory(staff=True)
|
|
source = SourceSystem.objects.create(code="moysklad", name="МойСклад")
|
|
product = Product.objects.create(sku="FISHKA", name="Фишка")
|
|
api_client.force_authenticate(user=user)
|
|
|
|
sale_response = api_client.post(
|
|
reverse("api:v1:commerce:sale-list"),
|
|
{
|
|
"source": source.pk,
|
|
"external_id": "order-1",
|
|
"occurred_at": timezone.now().isoformat(),
|
|
},
|
|
format="json",
|
|
)
|
|
assert sale_response.status_code == status.HTTP_201_CREATED
|
|
|
|
line_response = api_client.post(
|
|
reverse("api:v1:commerce:saleline-list"),
|
|
{
|
|
"sale": sale_response.data["id"],
|
|
"product": product.pk,
|
|
"external_id": "line-1",
|
|
"quantity": "2",
|
|
"gross_amount": "500.00",
|
|
"discount_amount": "50.00",
|
|
"cost_amount": "200.00",
|
|
},
|
|
format="json",
|
|
)
|
|
assert line_response.status_code == status.HTTP_201_CREATED
|
|
|
|
cost_response = api_client.post(
|
|
reverse("api:v1:commerce:cost-list"),
|
|
{
|
|
"source": source.pk,
|
|
"product": product.pk,
|
|
"cost_type": "commission",
|
|
"amount": "25.00",
|
|
"occurred_at": timezone.now().isoformat(),
|
|
},
|
|
format="json",
|
|
)
|
|
assert cost_response.status_code == status.HTTP_201_CREATED, cost_response.data
|
|
|
|
stock_response = api_client.post(
|
|
reverse("api:v1:commerce:stocksnapshot-list"),
|
|
{
|
|
"source": source.pk,
|
|
"product": product.pk,
|
|
"warehouse_external_id": "main",
|
|
"quantity": "10",
|
|
"reserved": "2",
|
|
"captured_at": timezone.now().isoformat(),
|
|
},
|
|
format="json",
|
|
)
|
|
assert stock_response.status_code == status.HTTP_201_CREATED
|
|
assert SaleLine.objects.filter(pk=line_response.data["id"]).exists()
|
|
assert Cost.objects.filter(pk=cost_response.data["id"]).exists()
|
|
assert StockSnapshot.objects.filter(pk=stock_response.data["id"]).exists()
|
|
|
|
|
|
def test_product_analytics_endpoint(api_client, user_factory):
|
|
user = user_factory()
|
|
source = SourceSystem.objects.create(code="insales", name="InSales")
|
|
product = Product.objects.create(sku="FISHKA", name="Фишка")
|
|
sale = Sale.objects.create(
|
|
source=source,
|
|
external_id="order-1",
|
|
occurred_at=timezone.now(),
|
|
channel="site",
|
|
)
|
|
SaleLine.objects.create(
|
|
sale=sale,
|
|
product=product,
|
|
external_id="line-1",
|
|
quantity=Decimal("1"),
|
|
gross_amount=Decimal("500"),
|
|
cost_amount=Decimal("200"),
|
|
)
|
|
api_client.force_authenticate(user=user)
|
|
|
|
response = api_client.get(
|
|
reverse("api:v1:commerce:product-analytics", args=[product.pk])
|
|
)
|
|
|
|
assert response.status_code == status.HTTP_200_OK
|
|
assert response.data["product_id"] == product.pk
|
|
assert response.data["monetary"][0]["net_revenue"] == Decimal("500")
|
|
assert response.data["monetary"][0]["contribution_margin"] == Decimal("300")
|