File size: 5,936 Bytes
ce8f04a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
"""Pre-gen data completeness gate."""
from __future__ import annotations

from core.projects.data_completeness import (
    STATUS_MISSING,
    STATUS_READY,
    STATUS_STRUCTURE,
    apply_project_facts,
    evaluate_data_completeness,
)
from core.projects.instrument_schema import build_instrument_schema


def test_eurogranty_missing_target_blocks():
    schema = build_instrument_schema(program_name="Granty na Eurogranty FENG.02.12")
    r = evaluate_data_completeness(
        external_context={
            "company_data": {
                "nip": "8721990161",
                "name": "AUTOPROCES",
                "pkd_codes": ["28.29.Z"],
                "msp_status": "mikro",
                "de_minimis": 2738,
                "closed_financial_year": True,
                "revenue_3y": [1, 2, 3],
                "employment_fte": 2,
            },
            "grounding_mode": "regulation",
            "program_dossier": {"readiness": {"level": "partial"}},
        },
        instrument_schema=schema,
        description="Przygotowanie do aplikacji UE",
    )
    assert r["status"] == STATUS_MISSING
    assert r["full_autopilot_allowed"] is False
    fields = {m["field"] for m in r["missing_fields"]}
    assert "eurogrant_call" in fields or "eurogrant_programme" in fields


def test_structure_only_allows_generate():
    r = evaluate_data_completeness(
        external_context={
            "grounding_mode": "structure_only",
            # complete eligibility so structure-only is not blocked by spine
            "company_data": {
                "nip": "8721990161",
                "msp_status": "mikro",
                "de_minimis_manual_eur": 1000,
            },
        },
        grounding_mode="structure_only",
    )
    assert r["status"] == STATUS_STRUCTURE
    assert r["ready_to_generate"] is True
    assert r["full_autopilot_allowed"] is True
    assert r.get("eligibility_spine_enforced") is True


def test_structure_only_still_gates_full_autopilot_on_large_firm(monkeypatch):
    """structure_only must not fail-open Full Autopilot past eligibility spine."""
    monkeypatch.setenv("ENABLE_ELIGIBILITY_SPINE", "true")
    r = evaluate_data_completeness(
        external_context={
            "grounding_mode": "structure_only",
            "company_data": {
                "nip": "8721990161",
                "msp_status": "duza",
                "de_minimis_manual_eur": 1000,
            },
        },
        grounding_mode="structure_only",
    )
    assert r["status"] == STATUS_STRUCTURE
    assert r["ready_to_generate"] is True  # structural consent retained
    assert r["full_autopilot_allowed"] is False
    assert r.get("eligibility_spine_enforced") is True
    assert any("duża" in b.lower() or "duza" in b.lower() for b in (r.get("eligibility") or {}).get("blockers") or []) or any(
        "duż" in w.lower() or "duza" in w.lower() or "large" in w.lower() or "MŚP" in w or "duża" in w
        for w in (r.get("warnings") or [])
    )


def test_structure_only_gates_exhausted_de_minimis(monkeypatch):
    monkeypatch.setenv("ENABLE_ELIGIBILITY_SPINE", "true")
    r = evaluate_data_completeness(
        external_context={
            "grounding_mode": "structure_only",
            "company_data": {
                "nip": "8721990161",
                "msp_status": "mikro",
                "sudop": {"configured": True, "de_minimis_total_eur": 300_000},
            },
        },
        grounding_mode="structure_only",
    )
    assert r["ready_to_generate"] is True
    assert r["full_autopilot_allowed"] is False
    assert (r.get("eligibility") or {}).get("de_minimis", {}).get("exhausted") is True


def test_structure_only_gates_unknown_de_minimis(monkeypatch):
    monkeypatch.setenv("ENABLE_ELIGIBILITY_SPINE", "true")
    r = evaluate_data_completeness(
        external_context={
            "grounding_mode": "structure_only",
            "company_data": {
                "nip": "8721990161",
                "msp_status": "mikro",
                # no sudop / manual de minimis
            },
        },
        grounding_mode="structure_only",
    )
    assert r["full_autopilot_allowed"] is False
    assert r.get("eligibility_spine_enforced") is True


def test_apply_facts_then_ready_eurogranty():
    schema = build_instrument_schema(program_name="Granty na Eurogranty")
    ext = {
        "company_data": {
            "nip": "8721990161",
            "name": "AUTOPROCES",
            "pkd_codes": ["28.29.Z"],
            "msp_status": "mikro",
            "de_minimis": 1000,
            "closed_financial_year": True,
            "revenue_3y": {"2023": 1, "2024": 2, "2025": 3},
            "employment_fte": 3,
        },
        "grounding_mode": "regulation",
        "program_dossier": {"readiness": {"level": "ready"}},
    }
    ext = apply_project_facts(
        ext,
        {
            "eurogrant_programme": "Horizon Europe Cluster 4",
            "eurogrant_call": "HORIZON-CL4-2026-AI-MANUFACTURING",
            "eurogrant_role": "samodzielny",
            "specialist_analysis_yes_no": "tak — analiza partnerów i stanu techniki",
            "lump_sum_amount_pln": 110852,
        },
    )
    r = evaluate_data_completeness(
        external_context=ext,
        instrument_schema=schema,
        description="Przygotowanie wniosku Horizon dla AI in manufacturing",
    )
    assert r["status"] == STATUS_READY
    assert r["full_autopilot_allowed"] is True
    assert not r["missing_fields"]


def test_missing_nip_blocks():
    schema = build_instrument_schema(program_type="SMART", program_name="SMART")
    r = evaluate_data_completeness(
        external_context={"company_data": {}, "grounding_mode": "regulation"},
        instrument_schema=schema,
        description="Projekt innowacyjny",
    )
    assert r["full_autopilot_allowed"] is False
    assert any(m["field"] == "nip" for m in r["missing_fields"])