Интегрирован wesp в сайт
CI / quality (push) Canceled after 0s

This commit is contained in:
влад
2026-07-17 12:57:18 +03:00
parent 5dfa06ddbe
commit 355c0ef9f1
883 changed files with 194576 additions and 177 deletions
+14
View File
@@ -30,3 +30,17 @@ def test_seed_ensures_ops_is_not_superuser():
refreshed = get_user_by_email("ops@compton.example")
assert refreshed is not None
assert refreshed.is_superuser is False
def test_seed_ensures_demo_enterprise():
run_seed(include_demo_pages=False)
from app.modules.sync.repository import get_enterprise_by_slug
ent = get_enterprise_by_slug("demo-farm")
assert ent is not None
admin = get_user_by_email("admin@compton.example")
assert admin is not None
from app.modules.sync import repository as sync_repo
assert sync_repo.get_member(admin.id, ent.id) is not None
@@ -25,6 +25,72 @@ def test_wesp_compat_feed_dispensers(client: TestClient):
assert isinstance(resp.json(), list)
def test_wesp_compat_feed_dispenser_crud(client: TestClient):
enterprise_id = make_enterprise(client)
login = client.post("/api/v1/auth/login", json={"email": "admin@compton.example", "password": "Admin1234"})
token = login.json()["access_token"]
session = client.post(
f"/api/v1/enterprise/enterprises/{enterprise_id}/session",
headers={"Authorization": f"Bearer {token}"},
)
token = session.json()["access_token"]
headers = {"Authorization": f"Bearer {token}"}
params = {"enterprise_id": enterprise_id}
create = client.post(
"/api/feed_dispensers",
headers=headers,
params=params,
json={"name": "Disp A", "farm": "Farm 1", "operator": "Op 1", "type": "dispenser"},
)
assert create.status_code == 200
dispenser_id = create.json()["id"]
listed = client.get("/api/feed_dispensers", headers=headers, params=params)
assert listed.status_code == 200
assert any(row["id"] == dispenser_id for row in listed.json())
update = client.put(
f"/api/feed_dispensers/{dispenser_id}",
headers=headers,
params=params,
json={"name": "Disp B", "farm": "Farm 2", "operator": "Op 2"},
)
assert update.status_code == 200
assert update.json().get("message")
period = client.post(
f"/api/feed_dispensers/{dispenser_id}/periods",
headers=headers,
params=params,
json={"name": "Morning"},
)
assert period.status_code == 200
period_id = period.json()["id"]
period_update = client.put(
f"/api/feed_dispensers/{dispenser_id}/periods/{period_id}",
headers=headers,
params=params,
json={"name": "Evening"},
)
assert period_update.status_code == 200
period_delete = client.delete(
f"/api/feed_dispensers/{dispenser_id}/periods/{period_id}",
headers=headers,
params=params,
)
assert period_delete.status_code == 200
delete = client.delete(
f"/api/feed_dispensers/{dispenser_id}",
headers=headers,
params=params,
)
assert delete.status_code == 200
def test_wesp_compat_components(client: TestClient):
enterprise_id = make_enterprise(client)
login = client.post("/api/v1/auth/login", json={"email": "admin@compton.example", "password": "Admin1234"})
@@ -40,6 +106,24 @@ def test_wesp_compat_components(client: TestClient):
assert isinstance(resp.json(), list)
def test_wesp_compat_component_feed_types(client: TestClient):
enterprise_id = make_enterprise(client)
login = client.post("/api/v1/auth/login", json={"email": "admin@compton.example", "password": "Admin1234"})
token = login.json()["access_token"]
session = client.post(
f"/api/v1/enterprise/enterprises/{enterprise_id}/session",
headers={"Authorization": f"Bearer {token}"},
)
token = session.json()["access_token"]
headers = {"Authorization": f"Bearer {token}"}
resp = client.get(f"/api/components/feed-types?enterprise_id={enterprise_id}", headers=headers)
assert resp.status_code == 200
body = resp.json()
assert isinstance(body.get("types"), list)
assert len(body["types"]) >= 4
assert body["types"][0]["value"]
def test_wesp_compat_recipe_includes_ingredients_and_unloading_groups(client: TestClient):
enterprise_id = make_enterprise(client)
login = client.post("/api/v1/auth/login", json={"email": "admin@compton.example", "password": "Admin1234"})
@@ -210,6 +294,22 @@ def test_wesp_compat_analytics_endpoints(client: TestClient):
assert alerts.status_code == 200
assert "items" in alerts.json()
fq_settings = client.get(f"/api/feed-quality/settings?enterprise_id={enterprise_id}", headers=headers)
assert fq_settings.status_code == 200
fq_body = fq_settings.json()
assert "settings" in fq_body
assert "defaults" in fq_body
assert fq_body["settings"]["loading"]["enabled"] is True
fq_put = client.put(
f"/api/feed-quality/settings?enterprise_id={enterprise_id}",
headers=headers,
json={"settings": {"loading": {"warning_pct": 12}}},
)
assert fq_put.status_code == 200
assert fq_put.json()["saved"] is True
assert fq_put.json()["settings"]["loading"]["warning_pct"] == 12.0
def test_wesp_compat_reports_list(client: TestClient):
from app.modules.zootech.report_apply import apply_report_change
@@ -354,3 +454,369 @@ def test_apply_report_change_feed_alert(client: TestClient):
assert row.alert_type == "UNDERLOAD"
assert row.message == "Underload detected"
assert row.payload_json
def test_wesp_compat_transfer_recipe_between_periods(client: TestClient):
enterprise_id = make_enterprise(client)
login = client.post("/api/v1/auth/login", json={"email": "admin@compton.example", "password": "Admin1234"})
token = login.json()["access_token"]
session = client.post(
f"/api/v1/enterprise/enterprises/{enterprise_id}/session",
headers={"Authorization": f"Bearer {token}"},
)
token = session.json()["access_token"]
headers = {"Authorization": f"Bearer {token}"}
params = {"enterprise_id": enterprise_id}
dispenser = client.post(
"/api/feed_dispensers",
headers=headers,
params=params,
json={"name": "Disp transfer", "farm": "Farm", "operator": "Op", "type": "dispenser"},
)
assert dispenser.status_code == 200
dispenser_id = dispenser.json()["id"]
period_a = client.post(
f"/api/feed_dispensers/{dispenser_id}/periods",
headers=headers,
params=params,
json={"name": "Morning"},
)
period_b = client.post(
f"/api/feed_dispensers/{dispenser_id}/periods",
headers=headers,
params=params,
json={"name": "Evening"},
)
assert period_a.status_code == 200
assert period_b.status_code == 200
period_a_id = period_a.json()["id"]
period_b_id = period_b.json()["id"]
recipe = client.post(
f"/api/periods/{period_a_id}/recipes",
headers=headers,
params=params,
json={
"name": "Trip A",
"heads_count": 100,
"mixing_time": 10,
"trip_percent": 100,
"ingredients": [],
"unloading_groups": [{"name": "G1", "distribution_type": "percent", "value": 100}],
},
)
assert recipe.status_code == 200
recipe_id = recipe.json()["id"]
transfer = client.post(
f"/api/feed_dispensers/{dispenser_id}/periods/{period_b_id}/recipes/{recipe_id}/transfer",
headers=headers,
params=params,
json={
"from_dispenser_id": dispenser_id,
"from_period_id": period_a_id,
"to_index": 0,
},
)
assert transfer.status_code == 200
assert transfer.json().get("success") is True
src = client.get(f"/api/periods/{period_a_id}/recipes", headers=headers, params=params)
dst = client.get(f"/api/periods/{period_b_id}/recipes", headers=headers, params=params)
assert src.status_code == 200
assert dst.status_code == 200
assert src.json() == []
assert len(dst.json()) == 1
assert dst.json()[0]["id"] == recipe_id
def test_wesp_compat_move_recipe_within_period(client: TestClient):
enterprise_id = make_enterprise(client)
login = client.post("/api/v1/auth/login", json={"email": "admin@compton.example", "password": "Admin1234"})
token = login.json()["access_token"]
session = client.post(
f"/api/v1/enterprise/enterprises/{enterprise_id}/session",
headers={"Authorization": f"Bearer {token}"},
)
token = session.json()["access_token"]
headers = {"Authorization": f"Bearer {token}"}
params = {"enterprise_id": enterprise_id}
dispenser = client.post(
"/api/feed_dispensers",
headers=headers,
params=params,
json={"name": "Disp move", "farm": "Farm", "operator": "Op", "type": "dispenser"},
)
assert dispenser.status_code == 200
dispenser_id = dispenser.json()["id"]
period = client.post(
f"/api/feed_dispensers/{dispenser_id}/periods",
headers=headers,
params=params,
json={"name": "Morning"},
)
assert period.status_code == 200
period_id = period.json()["id"]
def _create_recipe(name: str) -> str:
resp = client.post(
f"/api/periods/{period_id}/recipes",
headers=headers,
params=params,
json={
"name": name,
"heads_count": 100,
"mixing_time": 10,
"trip_percent": 100,
"ingredients": [],
"unloading_groups": [{"name": "G1", "distribution_type": "percent", "value": 100}],
},
)
assert resp.status_code == 200
return resp.json()["id"]
first_id = _create_recipe("First")
second_id = _create_recipe("Second")
move = client.put(
f"/api/recipes/{first_id}/move",
headers=headers,
params=params,
json={"from_index": 0, "to_index": 1, "period_id": period_id},
)
assert move.status_code == 200
assert move.json().get("success") is True
listed = client.get(f"/api/periods/{period_id}/recipes", headers=headers, params=params)
assert listed.status_code == 200
ids = [row["id"] for row in listed.json()]
assert ids == [second_id, first_id]
def test_wesp_compat_sklad(client: TestClient):
from datetime import date, timedelta
from app.modules.zootech.report_apply import apply_report_change
enterprise_id = make_enterprise(client)
login = client.post("/api/v1/auth/login", json={"email": "admin@compton.example", "password": "Admin1234"})
token = login.json()["access_token"]
session = client.post(
f"/api/v1/enterprise/enterprises/{enterprise_id}/session",
headers={"Authorization": f"Bearer {token}"},
)
token = session.json()["access_token"]
headers = {"Authorization": f"Bearer {token}"}
params = {"enterprise_id": enterprise_id}
component = client.post(
"/api/components",
headers=headers,
params=params,
json={"name": "Corn", "type": "grain", "dryMatter": 88, "protein": 9, "energy": 13, "price": 15},
)
assert component.status_code == 200
component_id = component.json()["id"]
today = date.today()
date_from = (today - timedelta(days=30)).isoformat()
date_to = today.isoformat()
empty = client.get("/api/sklad", headers=headers, params={**params, "date_from": date_from, "date_to": date_to})
assert empty.status_code == 200
assert empty.json()["items"] == []
apply_report_change(
enterprise_id,
"loading_report",
"lr-sklad-1",
"upsert",
{
"recipe_id": "recipe-1",
"recipe_name": "Mix",
"start_time": datetime.now(UTC).isoformat(),
"total_weight": 500.0,
"components": [
{"component_id": component_id, "component_name": "Corn", "actual_weight": 42.5},
],
},
1,
"h1",
)
report_only = client.get("/api/sklad", headers=headers, params={**params, "date_from": date_from, "date_to": date_to})
assert report_only.status_code == 200
items = report_only.json()["items"]
assert len(items) == 1
assert items[0]["component_id"] == component_id
assert items[0]["has_stock"] is False
assert items[0]["consumed_total_kg"] == 42.5
added = client.post("/api/sklad/add", headers=headers, params=params, json={"component_id": component_id})
assert added.status_code == 200
assert added.json()["success"] is True
updated = client.post(
f"/api/sklad/{component_id}",
headers=headers,
params=params,
json={"total_kg": 1000.0, "inflow_kg": 50.0},
)
assert updated.status_code == 200
assert updated.json()["success"] is True
stocked = client.get("/api/sklad", headers=headers, params={**params, "date_from": date_from, "date_to": date_to})
assert stocked.status_code == 200
stocked_items = stocked.json()["items"]
assert len(stocked_items) == 1
assert stocked_items[0]["has_stock"] is True
assert stocked_items[0]["remaining_kg"] == 1007.5
chart = client.get(
f"/api/sklad/{component_id}/chart",
headers=headers,
params={**params, "date_from": date_from, "date_to": date_to},
)
assert chart.status_code == 200
assert isinstance(chart.json().get("points"), list)
assert len(chart.json()["points"]) > 0
removed = client.delete(f"/api/sklad/{component_id}", headers=headers, params=params)
assert removed.status_code == 200
assert removed.json()["success"] is True
def test_wesp_compat_feed_accounting(client: TestClient):
from app.modules.zootech.report_apply import apply_report_change
enterprise_id = make_enterprise(client)
login = client.post("/api/v1/auth/login", json={"email": "admin@compton.example", "password": "Admin1234"})
token = login.json()["access_token"]
session = client.post(
f"/api/v1/enterprise/enterprises/{enterprise_id}/session",
headers={"Authorization": f"Bearer {token}"},
)
token = session.json()["access_token"]
headers = {"Authorization": f"Bearer {token}"}
params = {"enterprise_id": enterprise_id}
org = client.get("/api/feed-accounting/org-settings", headers=headers, params=params)
assert org.status_code == 200
body = org.json()
assert "organization_name" in body
assert "available_farms" in body
assert body["signatures"]["zootechnician"] is False
saved = client.put(
"/api/feed-accounting/org-settings",
headers=headers,
params=params,
json={
"organization_name": "Test Farm LLC",
"zootechnician": "Ivanov I.I.",
"warehouse_keeper": "Petrov P.P.",
},
)
assert saved.status_code == 200
assert saved.json().get("success") is True
apply_report_change(
enterprise_id,
"loading_report",
"lr-fa-1",
"upsert",
{
"recipe_id": "recipe-1",
"recipe_name": "Mix",
"start_time": "2026-06-15T10:00:00+00:00",
"total_weight": 500.0,
"components": [{"component_name": "Corn", "actual_weight": 25.0}],
},
1,
"h1",
)
preview = client.get(
"/api/feed-accounting/sp20-preview",
headers=headers,
params={**params, "month": "2026-06"},
)
assert preview.status_code == 200
preview_body = preview.json()
assert preview_body["lines_count"] == 1
assert preview_body["total_kg"] == 25.0
assert preview_body["has_requisites"] is True
xlsx = client.get(
"/api/feed-accounting/consumption.xlsx",
headers=headers,
params={**params, "date_from": "2026-06-01", "date_to": "2026-06-30"},
)
assert xlsx.status_code == 200
assert "spreadsheetml" in (xlsx.headers.get("content-type") or "")
assert len(xlsx.content) > 100
pdf = client.get(
"/api/feed-accounting/sp20.pdf",
headers=headers,
params={**params, "month": "2026-06"},
)
assert pdf.status_code == 200
assert (pdf.headers.get("content-type") or "").startswith("application/pdf")
assert pdf.content[:4] == b"%PDF"
def test_wesp_compat_mill_recipes_list_after_create(client: TestClient):
enterprise_id = make_enterprise(client)
login = client.post("/api/v1/auth/login", json={"email": "admin@compton.example", "password": "Admin1234"})
token = login.json()["access_token"]
session = client.post(
f"/api/v1/enterprise/enterprises/{enterprise_id}/session",
headers={"Authorization": f"Bearer {token}"},
)
token = session.json()["access_token"]
headers = {"Authorization": f"Bearer {token}"}
params = {"enterprise_id": enterprise_id}
mill = client.post(
"/api/feed_dispensers",
headers=headers,
params=params,
json={"name": "Mill 1", "farm": "Farm 1", "operator": "Op 1", "type": "mill"},
)
assert mill.status_code == 200
mill_id = mill.json()["id"]
empty = client.get(f"/api/feed_dispensers/{mill_id}/recipes", headers=headers, params=params)
assert empty.status_code == 200
assert empty.json() == []
created = client.post(
"/api/recipes",
headers=headers,
params=params,
json={
"name": "Mill Mix",
"heads_count": 50,
"mixing_time": 5,
"trip_percent": 100,
"dry_matter_locked": False,
"unloading_link_broken": False,
"dispenser_id": mill_id,
"ingredients": [],
"unloading_groups": [],
},
)
assert created.status_code == 200
recipe_id = created.json()["id"]
listed = client.get(f"/api/feed_dispensers/{mill_id}/recipes", headers=headers, params=params)
assert listed.status_code == 200
recipes = listed.json()
assert len(recipes) == 1
assert recipes[0]["id"] == recipe_id
assert recipes[0]["name"] == "Mill Mix"