participation() — единственное место формулы доли участия; totals с УК, group_totals по ГО, GoodwillTableOut.groups; экспорт берёт «Всего» и «Итого по листу» из ответа таблицы; доля 0–100 не больше двух знаков (DOC-259025 §5.2), ответ — два знака. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
158 lines
7.5 KiB
Python
158 lines
7.5 KiB
Python
"""Проверка ввода строки до обращения к хранилищу."""
|
|
|
|
from decimal import Decimal
|
|
from http import HTTPStatus
|
|
|
|
import pytest
|
|
from sqlalchemy import func, select
|
|
|
|
from src.api.dependencies.audit import RequestAudit
|
|
from src.common.exceptions import ConflictError, DomainValidationError
|
|
from src.models.audit_ex import AuditEvent
|
|
from src.models.enums import AuditAction
|
|
from src.models.kfo import Goodwill, KfoResult
|
|
from src.models.osv import OsvEntry
|
|
from src.models.statements import FinancialStatementValue
|
|
from src.schemas.goodwill import GoodwillRowIn
|
|
from src.services.goodwill import validate_decision
|
|
from tests.test_api_consolidated_osv import KA_RF
|
|
from tests.test_api_goodwill import goodwill_world
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"value", ["-1", "100.01", "100.0001", "80.123", "80,123", "12.3456", "1e2", "NaN", "Infinity", "abc", ""]
|
|
)
|
|
def test_invalid_share(value):
|
|
with pytest.raises(DomainValidationError) as error:
|
|
validate_decision(GoodwillRowIn(ownership_share=value, include_in_kfo=False))
|
|
assert error.value.code == "INVALID_SHARE"
|
|
|
|
|
|
@pytest.mark.parametrize("value", ["0", "100", "7.5", "80.12", "80,12"])
|
|
def test_valid_share(value):
|
|
result = validate_decision(GoodwillRowIn(ownership_share=value, include_in_kfo=True, comment=" Обоснование "))
|
|
assert str(result.ownership_share) == value.replace(",", ".")
|
|
assert result.comment == "Обоснование"
|
|
|
|
|
|
def test_required_fields():
|
|
assert validate_decision(GoodwillRowIn(ownership_share=None, include_in_kfo=False)).ownership_share is None
|
|
with pytest.raises(DomainValidationError) as error:
|
|
validate_decision(GoodwillRowIn(ownership_share=None, include_in_kfo=True, comment="Да"))
|
|
assert error.value.code == "INVALID_SHARE"
|
|
with pytest.raises(DomainValidationError) as error:
|
|
validate_decision(GoodwillRowIn(ownership_share="100", include_in_kfo=True, comment=" "))
|
|
assert error.value.code == "COMMENT_REQUIRED"
|
|
|
|
|
|
@pytest.mark.integration
|
|
@pytest.mark.fixtures
|
|
async def test_save_settings_and_disappearing_pair(db_session, auth_client, fixtures_dir):
|
|
|
|
w = await goodwill_world(db_session, fixtures_dir)
|
|
client = await auth_client(KA_RF)
|
|
base = f"/consolidations/versions/{w.version.id}"
|
|
path = base + f"/goodwill/{w.a.id}/{w.b.id}"
|
|
body = dict(ownership_share="80.12", include_in_kfo=True, comment="Подтверждено")
|
|
response = await client.put(path, json=body)
|
|
assert response.status_code == HTTPStatus.CONFLICT and response.json()["detail"]["code"] == "LOCK_REQUIRED"
|
|
assert (await client.post(base + "/lock")).status_code == HTTPStatus.CREATED
|
|
other = await auth_client("rka@armka.local")
|
|
rejected = await other.put(path, json=body)
|
|
assert rejected.status_code == HTTPStatus.CONFLICT
|
|
assert rejected.json()["detail"]["code"] == "LOCKED_BY_OTHER"
|
|
response = await client.put(path, json=body)
|
|
assert response.status_code == HTTPStatus.OK, response.text
|
|
row = response.json()
|
|
assert row["ownership_share"] == "80.12"
|
|
assert row["participation_amount"] == "80120000.00"
|
|
assert row["goodwill_amount"] == "189877300.00"
|
|
again = await client.put(path, json=body)
|
|
assert again.status_code == HTTPStatus.OK
|
|
assert await db_session.scalar(select(func.count()).select_from(Goodwill)) == 1
|
|
on = await client.put(base + "/goodwill-settings", json=dict(goodwill_enabled=True))
|
|
assert on.status_code == HTTPStatus.OK
|
|
assert on.json()["totals"]["goodwill_in_kfo_amount"] == "189877300.00"
|
|
audit_count = await db_session.scalar(
|
|
select(func.count()).select_from(AuditEvent).where(AuditEvent.action == AuditAction.GOODWILL_EDIT)
|
|
)
|
|
assert (
|
|
await client.put(base + "/goodwill-settings", json=dict(goodwill_enabled=True))
|
|
).status_code == HTTPStatus.OK
|
|
assert audit_count == await db_session.scalar(
|
|
select(func.count()).select_from(AuditEvent).where(AuditEvent.action == AuditAction.GOODWILL_EDIT)
|
|
)
|
|
off = await client.put(base + "/goodwill-settings", json=dict(goodwill_enabled=False))
|
|
assert off.json()["totals"]["goodwill_in_kfo_amount"] == "0.00"
|
|
assert off.json()["rows"][0]["ownership_share"] == "80.12"
|
|
body["include_in_kfo"] = False
|
|
assert (await client.put(path, json=body)).status_code == HTTPStatus.OK
|
|
assert (await client.get(base + "/goodwill")).json()["totals"]["goodwill_amount"] == "0.00"
|
|
entries = list(
|
|
(
|
|
await db_session.scalars(
|
|
select(OsvEntry).where(OsvEntry.account_code == "58.01", OsvEntry.owner_legal_entity_id == w.a.id)
|
|
)
|
|
).all()
|
|
)
|
|
for entry in entries:
|
|
entry.closing_debit = 0
|
|
await db_session.flush()
|
|
row = (await client.get(base + "/goodwill")).json()["rows"][0]
|
|
assert not row["has_investments"] and row["goodwill_amount"] is None
|
|
assert row["comment"] == "Подтверждено"
|
|
assert (await client.put(path, json=body)).status_code == HTTPStatus.OK
|
|
body["include_in_kfo"] = True
|
|
response = await client.put(path, json=body)
|
|
assert response.status_code == HTTPStatus.OK
|
|
assert response.json()["financial_investment_amount"] is None
|
|
assert response.json()["goodwill_amount"] is None
|
|
assert await db_session.scalar(select(func.count()).select_from(KfoResult)) == 0
|
|
|
|
|
|
@pytest.mark.integration
|
|
@pytest.mark.fixtures
|
|
async def test_mutation_validation_and_rollback(db_session, auth_client, fixtures_dir, monkeypatch):
|
|
|
|
w = await goodwill_world(db_session, fixtures_dir)
|
|
client = await auth_client(KA_RF)
|
|
base = f"/consolidations/versions/{w.version.id}"
|
|
path = base + f"/goodwill/{w.a.id}/{w.b.id}"
|
|
assert (await client.post(base + "/lock")).status_code == HTTPStatus.CREATED
|
|
body = dict(ownership_share="100", include_in_kfo=True, comment="")
|
|
response = await client.put(path, json=body)
|
|
assert (
|
|
response.status_code == HTTPStatus.UNPROCESSABLE_ENTITY
|
|
and response.json()["detail"]["code"] == "COMMENT_REQUIRED"
|
|
)
|
|
body.update(ownership_share="100.00001", comment="Да")
|
|
assert (await client.put(path, json=body)).json()["detail"]["code"] == "INVALID_SHARE"
|
|
assert await db_session.scalar(select(func.count()).select_from(Goodwill)) == 0
|
|
|
|
async def reject_audit(*args, **kwargs):
|
|
raise ConflictError("TEST_AUDIT_FAILURE", "Ошибка тестового аудита")
|
|
|
|
monkeypatch.setattr(RequestAudit, "record", reject_audit)
|
|
body["ownership_share"] = "100"
|
|
response = await client.put(path, json=body)
|
|
assert response.status_code == HTTPStatus.CONFLICT
|
|
assert await db_session.scalar(select(func.count()).select_from(Goodwill)) == 0
|
|
|
|
|
|
@pytest.mark.integration
|
|
@pytest.mark.fixtures
|
|
async def test_money_overflow_keeps_database_unchanged(db_session, auth_client, fixtures_dir):
|
|
w = await goodwill_world(db_session, fixtures_dir)
|
|
client = await auth_client(KA_RF)
|
|
base = f"/consolidations/versions/{w.version.id}"
|
|
assert (await client.post(base + "/lock")).status_code == HTTPStatus.CREATED
|
|
value = (await db_session.scalars(select(FinancialStatementValue))).one()
|
|
value.amount = Decimal("9999999999999999.99")
|
|
await db_session.flush()
|
|
response = await client.put(
|
|
base + f"/goodwill/{w.a.id}/{w.b.id}", json=dict(ownership_share="100", include_in_kfo=True, comment="Проверка")
|
|
)
|
|
assert response.status_code == HTTPStatus.UNPROCESSABLE_ENTITY
|
|
assert response.json()["detail"]["code"] == "MONEY_OUT_OF_RANGE"
|
|
assert await db_session.scalar(select(func.count()).select_from(Goodwill)) == 0
|