120 lines
4.2 KiB
Python
120 lines
4.2 KiB
Python
from pathlib import Path
|
|
import asyncio
|
|
import pytest
|
|
from app.core.config import settings
|
|
|
|
from app.economy.backend.router import (
|
|
SettlementRequest,
|
|
_build_settlement_preview,
|
|
_normalise_billing_method,
|
|
_settlement_label,
|
|
)
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def legacy_queue_mode(monkeypatch):
|
|
monkeypatch.setattr(settings, "WORKFLOWS_ENABLED", False)
|
|
|
|
|
|
def test_time_queue_normalises_legacy_settlement_names():
|
|
assert _normalise_billing_method("prepaid_card") == "prepaid"
|
|
assert _normalise_billing_method("fixed_price") == "subscription"
|
|
assert _normalise_billing_method(None) == "invoice"
|
|
assert _settlement_label("subscription") == "Abonnement / fast pris"
|
|
|
|
|
|
def test_time_queue_has_preview_and_safe_draft_tracking():
|
|
router_source = (ROOT / "app/economy/backend/router.py").read_text()
|
|
template_source = (ROOT / "app/economy/frontend/time_queue.html").read_text()
|
|
migration_source = (ROOT / "migrations/1018_economy_time_queue_settlement.sql").read_text()
|
|
|
|
assert '"/time-queue/preview-settlement"' in router_source
|
|
assert '"/time-queue/settle"' in router_source
|
|
assert "economy_order_draft_id" in router_source
|
|
assert "preview-settlement" in template_source
|
|
assert "Forhåndsvisning af afregning" in template_source
|
|
assert "economy_order_draft_id" in migration_source
|
|
|
|
|
|
def test_case_customer_is_authoritative_for_queue_and_settlement():
|
|
router_source = (ROOT / "app/economy/backend/router.py").read_text()
|
|
|
|
# A moved/corrected case must not be invoiced to the historic customer
|
|
# stored on a time entry.
|
|
assert "COALESCE(s.customer_id, effective_customer.hub_customer_id) AS customer_id" in router_source
|
|
assert "The case company is authoritative" in router_source
|
|
|
|
|
|
def test_time_queue_includes_solved_and_closed_cases_by_default(monkeypatch):
|
|
from app.economy.backend import router as economy_router
|
|
|
|
captured = {}
|
|
|
|
def fake_query(query, params):
|
|
captured["query"] = query
|
|
captured["params"] = params
|
|
return [{"id": 8, "case_status": "Løst"}]
|
|
|
|
monkeypatch.setattr(economy_router, "execute_query", fake_query)
|
|
result = asyncio.run(economy_router.list_hub_time_queue(
|
|
customer_id=None, status=None, case_state=None, billable=None, q=None, limit=500
|
|
))
|
|
|
|
assert result["items"][0]["case_status"] == "Løst"
|
|
assert "s.status" not in captured["query"].split("WHERE", 1)[1].split("ORDER BY", 1)[0]
|
|
|
|
|
|
def test_time_queue_has_explicit_closed_case_filter(monkeypatch):
|
|
from app.economy.backend import router as economy_router
|
|
|
|
captured = {}
|
|
monkeypatch.setattr(economy_router, "execute_query", lambda query, params: captured.update(query=query, params=params) or [])
|
|
asyncio.run(economy_router.list_hub_time_queue(
|
|
customer_id=None, status=None, case_state="closed", billable=None, q=None, limit=500
|
|
))
|
|
|
|
assert "'lukket', 'løst', 'closed', 'resolved', 'afsluttet', 'done'" in captured["query"]
|
|
assert "s.status" not in captured["params"]
|
|
|
|
|
|
def test_invoice_preview_uses_unrounded_draft_hours(monkeypatch):
|
|
from app.economy.backend import router as economy_router
|
|
|
|
monkeypatch.setattr(economy_router, "_selected_time_entries", lambda ids: [{
|
|
"id": 12,
|
|
"customer_id": 44,
|
|
"sag_id": 7,
|
|
"billing_method": "invoice",
|
|
"billable": True,
|
|
"approved_hours": 0.5,
|
|
"original_hours": 0.5,
|
|
"faktisk_tid_min": 18,
|
|
"case_title": "Support",
|
|
"customer_name": "Eksempel ApS",
|
|
}])
|
|
monkeypatch.setattr(
|
|
economy_router,
|
|
"_resolve_tmodule_customer_id",
|
|
lambda customer_id, sag_id: 44,
|
|
)
|
|
monkeypatch.setattr(
|
|
economy_router,
|
|
"execute_query_single",
|
|
lambda query, params: {"name": "Eksempel ApS", "hourly_rate": 1000},
|
|
)
|
|
|
|
preview = _build_settlement_preview(SettlementRequest(ids=[12], billing_method="invoice"))
|
|
|
|
assert preview["valid"] is True
|
|
assert preview["invoice_groups"] == [{
|
|
"customer_id": 44,
|
|
"customer_name": "Eksempel ApS",
|
|
"entries": [12],
|
|
"hours": 0.5,
|
|
"hourly_rate": 1000.0,
|
|
"amount_ex_vat": 500.0,
|
|
}]
|