bmc_hub/tests/test_economy_time_queue.py
Christian 28a92eeb55 Add tests for case tree structure, timeline UI, and relation tree
- Implement tests to ensure the case tree contains parent and child branches while preventing cycles.
- Verify that the timeline UI utilizes tree filters and cursor paging.
- Confirm that the relation tree is displayed as a separate tab with appropriate search filters and inline module content.

Enhance time tracking with prepaid card usage alerts

- Create a migration to track alerts for prepaid card usage at 80% and 100%.
- Implement triggers to generate cases when usage thresholds are reached, ensuring proper notifications and case management.

Introduce audit logging for time entry edits

- Modify the sync log to include time entry updates in the audit stream without altering existing records.

Establish tracking for vTiger case imports and exports

- Create tables to track cases imported from vTiger into Hub and exports from Hub to vTiger, ensuring idempotency and proper reference management.
2026-09-24 09:12:25 +02:00

113 lines
4.0 KiB
Python

from pathlib import Path
import asyncio
from app.economy.backend.router import (
SettlementRequest,
_build_settlement_preview,
_normalise_billing_method,
_settlement_label,
)
ROOT = Path(__file__).resolve().parents[1]
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,
}]